/*
 * Copyright (c) 2005 KOZUKA Masahiro  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#define _WIN32_WINNT 0x0501

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <time.h>

#define NI_MAXHOST      1025
#define NI_MAXSERV      32

typedef SSIZE_T ssize_t;

#else
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <unistd.h>
#include <errno.h>
#include <netdb.h>

#include <fcntl.h>

#include <netinet/in.h>

#endif

#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#else
#define POLLIN     0x001
#define POLLPRI    0x002
#define POLLOUT    0x004
#define POLLERR    0x008
#define POLLHUP    0x0010
#endif

#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#include "sctp.h"

#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP 132
#endif

#define MAXIMUM_NUMBER_OF_IN_STREAMS         17
#define MAXIMUM_NUMBER_OF_OUT_STREAMS        17

struct socks_fd {
	int type;
#define SOCKS_FD_TYPE_TCP_SOCKET	0
#define SOCKS_FD_TYPE_SCTP_SOCKET	1
#define SOCKS_FD_TYPE_SCTP_SCTPLIB	2
	union {
		struct {
#ifdef WIN32
			SOCKET _fd;
#else
			int _fd;
#endif
			sctp_userCallback _callback;
			int _mask;
			int _flags;
		} _bsd;
		struct {
			int _instance_id;
			unsigned int _assoc_id;
			SCTP_ulpCallbacks _callbacks;
			int _sndmax;
			int _flags;
		} _sctp;
	} bsd_or_sctp;
};
#define bsd_fd bsd_or_sctp._bsd._fd
#define bsd_callback bsd_or_sctp._bsd._callback
#define bsd_mask bsd_or_sctp._bsd._mask
#define bsd_flags bsd_or_sctp._bsd._flags
#define sctp_instance_id bsd_or_sctp._sctp._instance_id
#define sctp_assoc_id bsd_or_sctp._sctp._assoc_id
#define sctp_callbacks bsd_or_sctp._sctp._callbacks
#define sctp_sndmax bsd_or_sctp._sctp._sndmax
#define sctp_flags bsd_or_sctp._sctp._flags

#define FD_FLAGS_CONNECTING 0x01
#define FD_FLAGS_DATA_RECEIVED 0x02
#define FD_FLAGS_EXCEED_SEND 0x04

struct socks_server {
	int type;
	struct socks_fd fd;
	struct addrinfo *fwd;
	struct socks_server *next;
} *servers;

struct socks_conn {
	int initialized;
	int used;

	struct socks_server *server;
	int service_state;

	struct socks_fd fd;

	char *service_read_buf;
	size_t service_read_size, service_read_idx, service_read_checked_idx;
	size_t service_read_window_size;

	char *service_send_buf;
	size_t service_send_size, service_send_idx, service_send_sent_idx;

	int service_protocol;

	unsigned char service_auth;
	int service_auth_state;
	size_t service_auth_start_idx;
	
	int service_cmd;
	size_t service_cmd_start_idx;
	int service_cmd_state;

	char hbuf[NI_MAXHOST];
	char sbuf[NI_MAXSERV];
	char username[NI_MAXHOST]; /* XXX */
	char password[NI_MAXHOST]; /* XXX */

	struct addrinfo *bind_res;
	struct addrinfo *connect_res;

	struct socks_fd proxy_fd;

	char *proxy_read_buf;
	size_t proxy_read_size, proxy_read_idx, proxy_read_checked_idx;
	size_t proxy_read_window_size;
	int proxy_state;

	struct addrinfo *fwd;
} *connects;
int connects_num, connects_max, connects_idx;

/* Protocol Specific Data */
#define SOCKS_VERSION_4		0x04
#define SOCKS_VERSION_5		0x05

#define SOCKS_CMD_CONNECT	0x01
#define SOCKS_CMD_BIND		0x02
#define SOCKS_CMD_ASSOCIATE	0x03

#define SOCKS4_CMD_SIZE		1
#define SOCKS4_PORT_SIZE	2
#define SOCKS4_ADDR_SIZE	4

#define SOCKS5_AUTH_NOTHING	0x00
#define SOCKS5_AUTH_GSSAPI	0x01
#define SOCKS5_AUTH_PASSWORD	0x02
#define SOCKS5_AUTH_NO		0xFF

#define SOCKS5_AUTH_PASSWORD_VERSION	0x01
#define SOCKS5_AUTH_PASSWORD_OK		0x00
#define SOCKS5_AUTH_PASSWORD_NG		0x01

#define SOCKS5_CMD_UDP_ASSOCIATE	0x03
#define SOCKS4_CMD_SIZE			1

#define SOCKS5_ADDR_IPV4	0x01
#define SOCKS5_ADDR_IPV6	0x04
#define SOCKS5_ADDR_HOSTNAME	0x03
#define SOCKS5_ADDR_IPV4_SIZE	4
#define SOCKS5_ADDR_IPV6_SIZE	16

#define SOCKS5_PORT_SIZE	2

/* State */
#define SERVICE_STATE_FREE 0
#define SERVICE_STATE_START 1
#define SERVICE_STATE_READ_AUTH_REQUEST 2
#define SERVICE_STATE_DO_AUTH 3
#define SERVICE_STATE_READ_CMD_REQUEST 4
#define SERVICE_STATE_PREPARE_SERVICE 5
#define SERVICE_STATE_DO_SERVICE 6
#define SERVICE_STATE_FIN 7
#define SERVICE_STATE_WAIT_CLOSE 8
#define SERVICE_STATE_CLOSE 9

#define PROXY_STATE_FREE 0
#define PROXY_STATE_DO_SERVICE 1
#define PROXY_STATE_FIN 2
#define PROXY_STATE_WAIT_CLOSE 3
#define PROXY_STATE_CLOSE 4

#define AUTH_STATE_START 0
#define AUTH_STATE_PASSWORD_READ_UNAME 1
#define AUTH_STATE_PASSWORD_READ_PASS 2
#define AUTH_STATE_END 3

#define CMD_STATE_START		0
#define CMD_STATE_READ_CMD_REQUEST	1
#define CMD_STATE_READ_RSV	2
#define CMD_STATE_READ_ADDR	3
#define CMD_STATE_READ_PORT	4
#define CMD_STATE_READ_USER	5
#define CMD_STATE_END		6

/* Return code */
#define COMPLETE_READ 0
#define INCOMPLETE_READ 1
#define BAD_READ 2

#define SYSLOG_VERBOSE 2
#define SYSLOG_INFO 1
#define SYSLOG_ERROR 0

#define MAX_RCVBUF 1638400
#define MAX_SNDBUF 1638400
#define MAX_DATA_SIZE SCTP_MAXIMUM_DATA_LENGTH

FILE *socks_logfile;
int _priority = SYSLOG_INFO;
char *argv0;

void parse_args(int, char **);
void usage(char *argv0);
void set_logfile(const char *);
void vsyslog(int priority, const char *format, va_list ap);
void syslog(int , const char *, ...);

int socks_initialize(void);
int socks_stop(void);
int socks_register_server(char *, char *, char *, char *, char *, char *);
int socks_unregister_server(struct socks_server *);
struct socks_server *
socks_create_bsd_server(struct addrinfo **);
int socks_delete_bsd_server(struct socks_server *);
struct socks_server *
socks_create_server(struct addrinfo *);
int socks_delete_server(struct socks_server *);
    
void socks_server_bsd_handler(int, short int, short int *, void *);
void socks_server_bsd_accept_handler(struct socks_server *);
void * socks_server_accept_handler(unsigned int, int, unsigned int,
    unsigned short, unsigned short, int, void*);

void socks_start_service(struct socks_conn *);

void socks_service_bsd_handler(int, short int, short int *, void *);
void socks_service_bsd_read_handler(struct socks_conn *);
int socks_service_bsd_send_handler(struct socks_conn *);
void socks_service_bsd_close_handler(struct socks_conn *);

void socks_service_read_handler(unsigned int, unsigned short, unsigned int,
   unsigned short, unsigned int, unsigned int, unsigned int, void *);
int socks_service_send_handler(struct socks_conn *);
void socks_service_queue_status_handler(unsigned int, int, int, int, void *);
void socks_service_shutdown_handler(unsigned int, void*);
void socks_service_close_handler(unsigned int, void*);
void socks_service_lost_handler(unsigned int, unsigned short, void *);
void socks_service_asconf_handler(unsigned int, unsigned short, unsigned char[SCTP_MAX_IP_LEN], int, void *);
void socks_service_network_status_handler(unsigned int, short, unsigned char[SCTP_MAX_IP_LEN], unsigned short, void *);

void socks_service_read(struct socks_conn *);
int socks_service_send(struct socks_conn *, char *, size_t);
void socks_service_suspend_read(struct socks_conn *);
void socks_service_resume_read(struct socks_conn *);

int socks_close_service(struct socks_conn *);

void socks_free_service(struct socks_conn *);

int socks_parse_request(struct socks_conn *);

int socks5_parse_auth(struct socks_conn *);
void socks5_response_auth(struct socks_conn *);
int socks5_do_auth(struct socks_conn *);
int socks5_do_auth_password(struct socks_conn *);
int socks5_check_auth_password(char *, size_t, char *, size_t);
void socks5_response_auth_password(struct socks_conn *, unsigned char);

int socks4_parse_cmd(struct socks_conn *);
int socks4_parse_addr(struct socks_conn *);
void socks4_response_cmd(struct socks_conn *, unsigned char);

int socks5_parse_cmd(struct socks_conn *);
int socks5_parse_addr(struct socks_conn *);
void socks5_response_cmd(struct socks_conn *, unsigned char);
int socks5_pack_addr(struct addrinfo *, char *, size_t *);

void socks_realloc_buf(char **, size_t *, size_t);

void socks_initialize_proxy(struct socks_conn *);
void socks_initialize_connect_proxy(struct socks_conn *);
void socks_initialize_bind_proxy(struct socks_conn *);
void socks_initialize_udp_proxy(struct socks_conn *);

void socks_proxy_start_socks(struct socks_conn *);

void socks_proxy_bsd_handler(int, short int, short int *, void *);

void socks_proxy_bsd_established_handler(struct socks_conn *);
void *socks_proxy_established_handler(unsigned int, int, unsigned int,
    unsigned short, unsigned short, int, void*);

void socks_proxy_start(struct socks_conn *);

void socks_proxy_bsd_read_handler(struct socks_conn *);
int socks_proxy_bsd_send_handler(struct socks_conn *);
void socks_proxy_bsd_close_handler(struct socks_conn *);

void socks_proxy_read(struct socks_conn *);
int socks_proxy_send(struct socks_conn *, char *, size_t);
void socks_proxy_suspend_read(struct socks_conn *);
void socks_proxy_resume_read(struct socks_conn *);


void socks_proxy_read_handler(unsigned int, unsigned short, unsigned int,
   unsigned short, unsigned int, unsigned int, unsigned int, void *);
int socks_proxy_send_handler(struct socks_conn *);
void socks_proxy_queue_status_handler(unsigned int, int, int, int, void *);
void socks_proxy_shutdown_handler(unsigned int, void*);
void socks_proxy_close_handler(unsigned int, void*);
void socks_proxy_lost_handler(unsigned int, unsigned short, void *);
void socks_proxy_asconf_handler(unsigned int, unsigned short, unsigned char[SCTP_MAX_IP_LEN], int, void *);
void socks_proxy_network_status_handler(unsigned int, short, unsigned char[SCTP_MAX_IP_LEN], unsigned short, void *);

void socks_close_proxy(struct socks_conn *);

void socks_free_proxy(struct socks_conn *);

#ifdef WIN32
SOCKET bsd_socket(struct addrinfo **);
int bsd_bind(SOCKET, struct addrinfo *);
int bsd_connect(SOCKET, struct addrinfo *);
#else
int bsd_socket(struct addrinfo **);
int bsd_bind(int, struct addrinfo *);
int bsd_connect(int, struct addrinfo *);
#endif
int sctp_bind(struct addrinfo *, SCTP_ulpCallbacks *, void*);
unsigned int sctp_connect(int, struct addrinfo *, void *);

void socks_notify(const char *, ...);

#ifdef WIN32
void WindowNotify(const char *, va_list);
#endif

/* For Win32 Service */
#if defined(WIN32) && defined(_WIN32_WINNT) && defined(ENABLE_SERVICE)
int service_register = 1;
int service_install = 0;
int service_uninstall = 0;

void socks_ServiceInstall(void);
void socks_ServiceUnInstall(void);
void socks_ServiceRegister(void);
void WINAPI socks_ServiceMain(DWORD, LPTSTR *);
void socks_ServiceCheckStop(unsigned int, void *, void *);
void WINAPI socks_ServiceCtrlHandler(DWORD Opcode);

#endif
int native_sctp = 0;
int running = 0;


int main(int argc, char **argv)
{
	int i = 1;
	char *item = NULL, *sep = NULL;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV], proto[NI_MAXSERV];
	char fwd_hbuf[NI_MAXHOST], fwd_sbuf[NI_MAXSERV];
	int num = 0;
	int available = 0;
	int logfile_set = 0;

	while (i < argc && argv[i][0] == '-') {
		if (argv[i][0] != '-') {
			usage(argv[0]);
		}

		if ((strcmp(argv[i], "-h") == 0 ||
		    strcmp(argv[i], "-L") == 0 ||
		    strcmp(argv[i], "-U") == 0) &&
		    i + 1 < argc) {
			i++;
#if defined(WIN32) && defined(_WIN32_WINNT) && defined(ENABLE_SERVICE)
		} else if (strcmp(argv[i], "-d") == 0 ||
		    strcmp(argv[i], "-i") == 0 ||
		    strcmp(argv[i], "-u") == 0) {
#endif
		} else if (strcmp(argv[i], "-l") == 0) {
			i++;
			set_logfile(argv[i]);
			logfile_set++;
		} else if (strcmp(argv[i], "-v") == 0) {
			i++;
			_priority = SYSLOG_VERBOSE;
		} else {
			usage(argv[0]);
		}
		i++;
	}
	if (_priority == SYSLOG_VERBOSE && logfile_set < 1) {
		set_logfile(NULL);
	}

#ifndef HAVE_GUI
	if (sctp_initLibrary(
#ifdef WIN32
	    NULL
#endif
	    ) != SCTP_SUCCESS) {
		fprintf(stderr, "Can't initialize sctplib\n");
		return -1;
	}
#endif

	if (socks_initialize() < 0) {
		fprintf(stderr, "Can't initialize SOCKS\n");
		return -1;
	}

	i = 1;
	while (i < argc && argv[i][0] == '-') {
		/* Binding address ....*/
		if (strcmp(argv[i], "-h") == 0 &&
		    i + 1 < argc) {
			i++;
			sep = strchr(argv[i], '/');
			if (sep == NULL) {
				usage(argv[0]);
			}

			memset(hbuf, 0, sizeof(hbuf));
			if (sep - argv[i] > sizeof(hbuf) - 1) {
				fprintf(stderr, "Parsing for \"-h\", but too long: \"%s\"\n", argv[i]);
				return -1;
			}
			memcpy(hbuf, argv[i], sep - argv[i]);

			item = sep + 1;
			sep = strchr(item, '/');
			if (sep == NULL) {
				usage(argv[0]);
			}

			memset(sbuf, 0, sizeof(sbuf));
			if (sep - argv[i] > sizeof(sbuf) - 1) {
				fprintf(stderr, "Parsing for \"-h\", but too long: \"%s\"\n", argv[i]);
				return -1;
			}
			memcpy(sbuf, item, sep - item);

			if (strlen(sep + 1) > sizeof(proto) - 1) {
				fprintf(stderr, "Parsing for \"-h\", but too long: \"%s\"\n", argv[i]);
				return -1;
			}
			strncpy(proto, sep + 1, sizeof(proto));

			if (socks_register_server(hbuf, sbuf, proto,
				NULL, NULL, NULL) == 0) {
				available++;
			}
			num++;
		} else if (strcmp(argv[i], "-L") == 0 &&
		    i + 1 < argc) {
			i++;
			sep = strchr(argv[i], '/');
			if (sep == NULL) {
				usage(argv[0]);
			}

			memset(sbuf, 0, sizeof(sbuf));
			if (sep - argv[i] > sizeof(sbuf) - 1) {
				fprintf(stderr, "Too long#1 %s\n", argv[i]);
				return -1;
			}
			memcpy(sbuf, argv[i], sep - argv[i]);

			item = sep + 1;
			sep = strchr(item, '/');
			if (sep == NULL) {
				usage(argv[0]);
			}

			memset(fwd_hbuf, 0, sizeof(fwd_hbuf));
			if (sep - item > sizeof(fwd_hbuf) - 1) {
				fprintf(stderr, "Too long#2 %s\n", argv[i]);
				return -1;
			}
			memcpy(fwd_hbuf, item, sep - item);

			item = sep + 1;
			memset(fwd_sbuf, 0, sizeof(fwd_sbuf));
			if (strlen(item) > sizeof(fwd_sbuf) -1) {
				fprintf(stderr, "Too long#3 %s\n", argv[i]);
				return -1;
			}
			strncpy(fwd_sbuf, item, sizeof(fwd_sbuf));

			if (socks_register_server(NULL, sbuf, "tcp",
				fwd_hbuf, fwd_sbuf, "sctp") == 0) {
				available++;
			}
			num++;
#if defined(WIN32) && defined(_WIN32_WINNT) && defined(ENABLE_SERVICE)
		} else if (strcmp(argv[i], "-d") == 0) {
			service_register = 0;
		} else if (strcmp(argv[i], "-i") == 0) {
			socks_ServiceInstall();
			return 0;
		} else if (strcmp(argv[i], "-u") == 0) {
			socks_ServiceUnInstall();
			return 0;
#endif
		} else if (strcmp(argv[i], "-l") == 0) {
			i++;
		} else if (strcmp(argv[i], "-v") == 0) {
			;
		} else {
			usage(argv[0]);
		}
		i++;
	}

	if (i != argc) {
		usage(argv[0]);
	}
	if (num == 0) {
		if (socks_register_server(NULL, "1080", "tcp", NULL, NULL, NULL) == 0) {
			available++;
		}
	}

	if (available == 0) {
		syslog(SYSLOG_ERROR, "no available service!\n");
		return -1;
	}

#if defined(WIN32) && defined(_WIN32_WINNT) && defined(ENABLE_SERVICE)
	if (service_register) {
		socks_ServiceRegister();
	} else 
#endif
#ifndef HAVE_GUI
	for (;;) {
		sctp_eventLoop();
	}
#endif
	return 0;
}


#if defined(WIN32) && defined(_WIN32_WINNT) && defined(ENABLE_SERVICE)
void
socks_ServiceInstall(void)
{
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;
	TCHAR szPath[MAX_PATH];

	syslog(SYSLOG_VERBOSE, "[socks_ServiceInstall]\n");
	if(!GetModuleFileName(NULL, szPath, MAX_PATH))
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceInstall] GetModuleFileName failed: %d\n", GetLastError()); 
		return;
	}

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (schSCManager == NULL)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceInstall] OpenSCManager failed: %d\n", GetLastError());
		return;
	}

	schService = CreateService(schSCManager, TEXT("socks"), TEXT("socks"),
	    SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
	    SERVICE_ERROR_NORMAL, szPath, NULL, NULL, NULL, NULL, NULL);

	if (schService == NULL)
	{
		CloseServiceHandle(schSCManager);
		syslog(SYSLOG_ERROR, "[socks_ServiceInstall] CreateService failed: %d\n", GetLastError());
		return;
	} else
	{
		CloseServiceHandle(schSCManager);
		CloseServiceHandle(schService);
		return;
	}
}


void
socks_ServiceUnInstall(void)
{
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	syslog(SYSLOG_VERBOSE, "[socks_ServiceUnInstall]\n");

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (schSCManager == NULL)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceUnInstall] OpenSCManager failed: %d\n", GetLastError());
		return;
	}

	schService = OpenService(schSCManager, TEXT("socks"), DELETE);
	if (schService == NULL)
	{
		CloseServiceHandle(schSCManager);
		syslog(SYSLOG_ERROR, "[socks_ServiceUnInstall] CreateService failed: %d\n", GetLastError());
		return;
	}

	if (DeleteService(schService) != 0)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceUnInstall] DeleteService failed: %d\n", GetLastError());
		CloseServiceHandle(schSCManager);
		CloseServiceHandle(schService);
		return;
	}

	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);
}


void
socks_ServiceRegister(void)
{
	SERVICE_TABLE_ENTRY DispatchTable[] = 
	{ 
	    { TEXT("socks"), socks_ServiceMain }, 
	    { NULL, NULL } 
	};

	syslog(SYSLOG_VERBOSE, "[socks_ServiceRegister]\n");

	if (StartServiceCtrlDispatcher(DispatchTable) == 0)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceRegister] StartServiceCtrlDispatcher failed: %d\n",
		    GetLastError());
	}
}


SERVICE_STATUS gStatus;
SERVICE_STATUS_HANDLE ghStatus = NULL;

void WINAPI
socks_ServiceMain(DWORD argc, LPTSTR *argv)
{
	unsigned int uiTimerId = 0;

	syslog(SYSLOG_VERBOSE, "[socks_ServiceMain]\n");

	memset(&gStatus, 0, sizeof(gStatus));

	gStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	gStatus.dwCurrentState = SERVICE_START_PENDING;
	gStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
	    SERVICE_ACCEPT_PAUSE_CONTINUE;
	gStatus.dwWin32ExitCode = 0;
	gStatus.dwServiceSpecificExitCode = 0;
	gStatus.dwCheckPoint = 0;
	gStatus.dwWaitHint = 0;

	ghStatus = RegisterServiceCtrlHandlerEx(TEXT("socks"),
	    (LPHANDLER_FUNCTION_EX)socks_ServiceCtrlHandler, NULL);
	if (ghStatus == NULL)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceMain] RegisterServiceCtrlHandlerEx failed: %d\n",
		    GetLastError());
		return;
	}

	gStatus.dwCurrentState = SERVICE_RUNNING;
	gStatus.dwWin32ExitCode = NO_ERROR;
	gStatus.dwWaitHint = 1000;
	if (SetServiceStatus(ghStatus, &gStatus) == 0)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceMain] SetServiceStatus failed: %d\n", GetLastError());
		return;
	}

	for (;;)
	{
		sctp_eventLoop();
	}
}


void WINAPI
socks_ServiceCtrlHandler(DWORD Opcode)
{
	syslog(SYSLOG_VERBOSE, "[socks_ServiceCtrlHandler]\n");

	switch (Opcode)
	{
	case SERVICE_CONTROL_PAUSE:
		gStatus.dwCurrentState = SERVICE_PAUSED;
		break;
	case SERVICE_CONTROL_CONTINUE:
		gStatus.dwCurrentState = SERVICE_RUNNING;
		break;
	case SERVICE_CONTROL_STOP:
		gStatus.dwCurrentState = SERVICE_STOP_PENDING;
		break;
	case SERVICE_CONTROL_INTERROGATE:
		break;
	default:
		break;
	}

	if (SetServiceStatus(ghStatus, &gStatus) == 0)
	{
		syslog(SYSLOG_ERROR, "[socks_ServiceCtrlHandler] SetServiceStatus failed: %d\n", GetLastError());
	}

	if (gStatus.dwCurrentState == SERVICE_STOP_PENDING)
	{
		gStatus.dwCurrentState = SERVICE_STOPPED;
		if (SetServiceStatus(ghStatus, &gStatus) == 0)
		{
			syslog(SYSLOG_ERROR, "[socks_ServiceCtrlHandler] SetServiceStatus failed: %d\n", GetLastError());
		}
	}
}
#endif


/* Usage. */
void
usage(char *argv0)
{
	fprintf(stderr, "usage: %s\n"
	    "[-h hostname/port/{tcp,sctp}] /* Convert from {tcp,sctp} to {sctp,tcp} */\n"
	    "[-L bind_port/forward_hostname/forward_port] /* Forward socks via sctp */\n"
	    "[-l logfile]\n"
	    , argv0);
	exit(1);
}


int adl_gettime(struct timeval *);

void
set_logfile(const char *filename)
{
	char _filename[255];

	if (filename == NULL) {
		struct timeval now;
		struct tm *the_time;

		adl_gettime(&now);
		the_time = localtime((time_t *)&(now.tv_sec));
#ifdef WIN32
		_snprintf(_filename, sizeof(_filename),
#else
		snprintf(_filename, sizeof(_filename),
#endif
		    "socks-%04d%02d%02d%02d%02d%02d.log",
		    the_time->tm_year + 1900, the_time->tm_mon + 1, the_time->tm_mday,
		    the_time->tm_hour, the_time->tm_min, the_time->tm_sec);
	}

	fprintf(stderr, "Logging SOCKS info to file %s\n",
	    filename != NULL ? filename : _filename);
	socks_logfile = fopen(filename != NULL ? filename : _filename, "a");
	if (socks_logfile == NULL) {
		fprintf(stderr, "Can't open %s\n", _filename);
		exit(-1);
	}
}


void
vsyslog(int priority, const char *format, va_list ap)
{
	struct timeval tv;
	struct tm *the_time;

	if (_priority < priority || socks_logfile == NULL) {
		return;
	}
	adl_gettime(&tv);
	the_time = localtime((time_t *) & (tv.tv_sec));

	fprintf(socks_logfile, "%02d:%02d:%02d.%03d - ",
	    the_time->tm_hour,
	    the_time->tm_min, the_time->tm_sec, (int) (tv.tv_usec / 1000));
	vfprintf(socks_logfile, format, ap);
	fflush(socks_logfile);
}


void
syslog(int priority, const char *format, ...)
{
	va_list ap;
	va_start(ap, format);

	vsyslog(priority, format, ap);

	va_end(ap);
}


/* Initialize Socks 4/5 server. */
int
socks_initialize(void)
{
	int i;

	connects_max = 8192;
	connects = malloc(sizeof(*connects) * connects_max);
	if (connects == NULL) {
		syslog(SYSLOG_ERROR, "[socks_initialize] malloc failed\n");
		exit(-1);
	}
	memset(connects, 0, sizeof(*connects) * connects_max);
	for (i = 0; i < connects_max; i++)
	{
	}
	connects_idx = 0;
	return 0;
}


int socks_stop(void)
{
	struct socks_server *server, *next_server;

	syslog(SYSLOG_VERBOSE, "[socks_stop]\n");

	server = servers;
	while (server != NULL) {
		next_server = server->next;
		socks_unregister_server(server);
		server = next_server;
	}
	servers = NULL;
	free(connects);
	return 0;
}


/* Initialize listening sockets. */
int
socks_register_server(char *hostname, char *servname, char *proto,
    char *fwd_hostname, char *fwd_servname, char *fwd_proto)
{
	int error = 0;
	struct addrinfo hints, *res = NULL, *res0 = NULL;
	struct addrinfo fwd_hints, *fwd_res = NULL, *fwd_res0 = NULL;
	unsigned char protocol = 0, fwd_protocol = 0;
	struct socks_server *server = NULL, *new_server = NULL;

	syslog(SYSLOG_VERBOSE, "Listening %s/%s/%s, Forwarding:%s\n",
	    hostname ? hostname : "::",
	    servname ? servname : "1080",
	    proto,
	    fwd_hostname != NULL ? "YES" : "NO");

	if (proto != NULL &&
	    strcmp(proto, "sctp") == 0) {
		protocol = IPPROTO_SCTP;
	}

	if (fwd_proto != NULL &&
	    strcmp(fwd_proto, "sctp") == 0) {
		fwd_protocol = IPPROTO_SCTP;
	}

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP; /* XXX */

	error = getaddrinfo(hostname, servname ? servname : "1080", &hints, &res0);
	if (error < 0)
	{
		syslog(SYSLOG_ERROR, "[socks_register_server] %s/%s %s",
		    hostname ? hostname : "",
		    servname ? servname : "1080",
		    gai_strerror(error));
		return -1;
	}
	for (res = res0; res; res = res->ai_next) {
		res->ai_protocol = protocol;
	}

	if (fwd_hostname != NULL || fwd_servname != NULL) {
		memset(&fwd_hints, 0, sizeof(fwd_hints));
		fwd_hints.ai_family = PF_UNSPEC;
		fwd_hints.ai_socktype = SOCK_STREAM;
		fwd_hints.ai_protocol = IPPROTO_TCP; /* XXX */

		error = getaddrinfo(fwd_hostname, fwd_servname,
		    &fwd_hints, &fwd_res0);
		if (error < 0) {
			syslog(SYSLOG_ERROR, "[socks_register_server] getaddrinfo failed for forwading(%s/%s): %s\n",
			    fwd_hostname ? fwd_hostname : "",
			    fwd_servname ? fwd_servname : "0",
			    gai_strerror(error));
			freeaddrinfo(res0);
			return -1;
		}
		if (fwd_res0 == NULL) {
			syslog(SYSLOG_ERROR, "[socks_register_server] getaddrinfo return no res for forwading(%s/%s)\n",
			    fwd_hostname ? fwd_hostname : "",
			    fwd_servname ? fwd_servname : "0");
			freeaddrinfo(res0);
			return -1;
		}
		for (fwd_res = fwd_res0; fwd_res; fwd_res = fwd_res->ai_next) {
			fwd_res->ai_protocol = protocol;
		}
	}

	/* Try to bind socket */
	res = res0;
	while (res != NULL) {
		new_server = socks_create_bsd_server(&res);
		if (new_server != NULL) {
			syslog(SYSLOG_VERBOSE, "[socks_register_server] register fd(%d)\n",
			    new_server->fd.bsd_fd);
		} else {
			if (res != NULL) {
				res = res->ai_next;
			}
			continue;
		}
		if (res->ai_protocol == IPPROTO_SCTP) {
			native_sctp = 1; /* Kernel supports SCTP */
		}
		new_server->fwd = fwd_res0;

		if (servers == NULL) {
			servers = server = new_server;
		} else {
			server->next = new_server;
			server = new_server;
		}
		if (res != NULL) {
			res = res->ai_next;
		}
	}

	if (native_sctp) {
		return 0;
	}

	/* Try to do SCTP by sctplib. */
	new_server = socks_create_server(res0);
	if (new_server != NULL) {
		new_server->fwd = fwd_res0;
		syslog(SYSLOG_VERBOSE, "[socks_register_server] register association(%d)\n",
		    new_server->fd.bsd_fd);
		if (servers == NULL) {
			servers = server = new_server;
		} else {
			server->next = new_server;
			server = new_server;
		}
	}

	if (servers == NULL) {
		syslog(SYSLOG_ERROR, "[socks_register_server] no valid server!!\n");
		if (res0 != NULL) {
			freeaddrinfo(res0);
		}
		if (fwd_res0 != NULL) {
			freeaddrinfo(fwd_res0);
		}
		return -1;
	}

	return 0;
}


int
socks_unregister_server(struct socks_server *server)
{
	struct socks_conn *sc;
	int i;

	syslog(SYSLOG_VERBOSE, "[socks_unregister_server] server=%p\n",
	    server);
	for (i = 0; i < connects_max; i++) {
		sc = &connects[i];
		if (sc->server != server) {
			continue;
		}
		socks_close_service(sc);
		socks_close_proxy(sc);
	}

	syslog(SYSLOG_VERBOSE, "type = %d\n", server->fd.type);
	switch (server->fd.type) {
	case SOCKS_FD_TYPE_TCP_SOCKET:
	case SOCKS_FD_TYPE_SCTP_SOCKET:
		socks_delete_bsd_server(server);
		break;
	case SOCKS_FD_TYPE_SCTP_SCTPLIB:
		socks_delete_server(server);
		break;
	default:
		break;
	}

	free(server);
	return 0;
}


struct socks_server *
socks_create_bsd_server(struct addrinfo **resP)
{
	int error = 0;
#ifdef WIN32
	SOCKET s = INVALID_SOCKET;
#else
	int s = -1;
#endif
	struct addrinfo *res;
	struct socks_server *server;

	res = *resP;
	s = bsd_socket(&res);
	*resP = res;
#ifdef WIN32
	if (s == INVALID_SOCKET) {
#else
	if (s == -1) {
#endif
		return NULL;
	}

	if (bsd_bind(s, res) < 0) {
#ifdef WIN32
		closesocket(s);
#else
		close(s);
#endif
		return NULL;
	}

	if (listen(s, 10) < 0) {
#ifdef WIN32
		error = WSAGetLastError();
		closesocket(s);
#else
		error = errno;
		close(s);
#endif
		syslog(SYSLOG_ERROR, "[socks_create_bsd_server] listen failed: %d\n",
		    error);
		return NULL;
	}

	server = (struct socks_server *)malloc(sizeof(*server));
	if (server == NULL) {
		syslog(SYSLOG_ERROR, "[socks_create_bsd_server] malloc failed.\n");
		return NULL;
	}
	memset(server, 0, sizeof(*server));

	if (res->ai_protocol == IPPROTO_SCTP) {
		server->fd.type = SOCKS_FD_TYPE_SCTP_SOCKET;
	} else {
		server->fd.type = SOCKS_FD_TYPE_TCP_SOCKET;
	}

	server->fd.bsd_fd = s;
	server->fd.bsd_mask = POLLIN;
	server->fd.bsd_callback = &socks_server_bsd_handler;

	sctp_registerUserCallback((int)server->fd.bsd_fd,
	    server->fd.bsd_callback,
	    server, server->fd.bsd_mask);

	return server;
}

int
socks_delete_bsd_server(struct socks_server *server)
{
	syslog(SYSLOG_VERBOSE, "[socks_delete_bsd_server] server=%p\n",
	    server);
	sctp_unregisterUserCallback(server->fd.bsd_fd);
#ifdef WIN32
	closesocket(server->fd.bsd_fd);
#else
	close(server->fd.bsd_fd);
#endif
	return 0;
}


struct socks_server *
socks_create_server(struct addrinfo *res)
{
	struct socks_server *server = NULL;
	int instance_id = -1;
	SCTP_InstanceParameters instanceParams;
	int result;

	server = (struct socks_server *)malloc(sizeof(*server));
	if (server == NULL) {
		syslog(SYSLOG_ERROR, "[socks_create_server] malloc failed #2\n");
		return NULL;
	}
	memset(server, 0, sizeof(*server));

	server->fd.type = SOCKS_FD_TYPE_SCTP_SCTPLIB;
	server->fd.sctp_callbacks.communicationUpNotif =
	    &socks_server_accept_handler;
	server->fd.sctp_callbacks.communicationLostNotif =
	    &socks_service_lost_handler;
	server->fd.sctp_callbacks.peerShutdownReceivedNotif =
	    &socks_service_shutdown_handler;
	server->fd.sctp_callbacks.shutdownCompleteNotif =
	    &socks_service_close_handler;
	server->fd.sctp_callbacks.asconfStatusNotif =
	    &socks_service_asconf_handler;
	server->fd.sctp_callbacks.dataArriveNotif =
	    &socks_service_read_handler;
	server->fd.sctp_callbacks.queueStatusChangeNotif =
	    &socks_service_queue_status_handler;
	server->fd.sctp_callbacks.networkStatusChangeNotif =
	    &socks_service_network_status_handler;

	instance_id = sctp_bind(res, &server->fd.sctp_callbacks, server);
	if (instance_id < 0) {
		syslog(SYSLOG_ERROR, "[socks_create_server] sctp_bind failed\n");
		free(server);
		return NULL;
	}

	if ((result = sctp_getAssocDefaults(instance_id, &instanceParams)) == SCTP_SUCCESS) {
		instanceParams.assocMaxRetransmits = 1000;
		instanceParams.pathMaxRetransmits = 500;
		instanceParams.maxSendQueue = 1000;
		instanceParams.myRwnd = MAX_DATA_SIZE;
		if ((result = sctp_setAssocDefaults(instance_id, &instanceParams)) != SCTP_SUCCESS) {
			syslog(SYSLOG_ERROR, "[socks_create_server] sctp_setAssocDefaults failed: %d\n",
			    result);
		}
	} else {
		syslog(SYSLOG_ERROR, "[socks_create_server] sctp_getAssocDefaultsfailed: %d\n",
		    result);
	}

	if (result != SCTP_SUCCESS) {
		sctp_unregisterInstance(instance_id);
		free(server);
		return NULL;
	}

	server->fd.sctp_instance_id = instance_id;
	server->fd.sctp_sndmax = instanceParams.maxSendQueue;

	return server;
}


int
socks_delete_server(struct socks_server *server)
{
	syslog(SYSLOG_VERBOSE, "[socks_delete_server] server=%p\n",
	    server);
	sctp_unregisterInstance(server->fd.sctp_instance_id);
	return 0;
}


/* Callback function for accepting. */
void
socks_server_bsd_handler(int fd, short int mask, short int *pmask, void *data)
{
	struct socks_server *server = (struct socks_server *)data;
	syslog(SYSLOG_VERBOSE, "[socks_server_bsd_handler] fd(%d) event(%d)\n",
	    fd, mask);

	socks_server_bsd_accept_handler(server);
}


void
socks_server_bsd_accept_handler(struct socks_server *server)
{
	struct sockaddr_storage addr;
	socklen_t addrlen = sizeof(addr);
#ifdef WIN32
	SOCKET s = INVALID_SOCKET;
#else
	int s = -1;
#endif
	struct socks_conn *sc;
	int error = 0;

	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
	unsigned long nonblock_flag = 0;

	syslog(SYSLOG_VERBOSE, "[socks_server_bsd_accept_handler] Try to accept(%d)\n",
	    server->fd.bsd_fd);
	s = accept(server->fd.bsd_fd, (struct sockaddr *)&addr, &addrlen);
#ifdef WIN32
	if (s == INVALID_SOCKET) {
		error = WSAGetLastError();
#else
	if (s < 0) {
		error = errno;
#endif
		syslog(SYSLOG_ERROR, "[socks_server_bsd_accept_handler] accept failed: %d\n",
		    error);
		return;
	}

	if (getnameinfo((struct sockaddr *)&addr, addrlen, hbuf, sizeof(hbuf),
		sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
		syslog(SYSLOG_ERROR, "[socks_server_bsd_accept_handler] &addr(%p), addrlen(%d) invalid sockaddr\n",
		    &addr, addrlen);
	}
	syslog(SYSLOG_VERBOSE, "[socks_server_bsd_accept_handler] accepted fd(%d) from %s/%s\n",
	    s, hbuf, sbuf);
	/* Set Non-blocking mode. */
#ifdef WIN32
	nonblock_flag = 1;
	if (ioctlsocket(s, FIONBIO, &nonblock_flag) == SOCKET_ERROR) {
		error = WSAGetLastError();
		syslog(SYSLOG_ERROR, "[socks_server_bsd_accept_handler] ioctlsocket(%d) failed: %d\n",
		    s, error);
		closesocket(s);
		return;
	}
#else
	nonblock_flag = fcntl(s, F_GETFL, 0);
	if (nonblock_flag < 0) {
		syslog(SYSLOG_ERROR, "[socks_server_bsd_accept_handler] fcntl(%d) F_GETFL failed: %d\n",
		    s, errno);
		close(s);
		return;
	}

	if (fcntl(s, F_SETFL, nonblock_flag | O_NONBLOCK) < 0) {
		syslog(SYSLOG_ERROR, "[socks_server_bsd_accept_handler] fcntl(%d) F_SETFL failed: %d\n",
		    s, errno);
#ifdef WIN32
		closesocket(s);
#else
		close(s);
#endif
		return;
	}
#endif

	/* Find a free connection data. */
	if (connects_idx < connects_max) {
		sc = &connects[connects_idx];
		connects_idx++;
	} else {
		int i;
		for (i = 0; i < connects_idx; i++) {
			if (connects[i].used == 0) {
				break;
			}
		}
		if (i == connects_max) {
			syslog(SYSLOG_ERROR, "[socks_server_bsd_accept_handler] no available resouse for fd(%d)\n", s);
#ifdef WIN32
			closesocket(s);
#else
			close(s);
#endif
			return;
		}
		sc = &connects[i];
		connects_idx = i + 1;
	}
	sc->used = 1;

	sc->server = server;
	sc->fd.type = server->fd.type;
	sc->fd.bsd_fd = s;
	sc->fd.bsd_mask = POLLIN;
	sc->fd.bsd_callback = &socks_service_bsd_handler;

	syslog(SYSLOG_VERBOSE, "[socks_server_bsd_accept_handler] sc(%p) for fd(%d) type(%d)\n",
	    sc, s, sc->fd.type);

	socks_notify("[ACCEPT] id=%p, from_addr=%s, from_port=%s\n",
	    sc, hbuf, sbuf);
	sc->fwd = server->fwd;
	socks_start_service(sc);

	sctp_registerUserCallback((int)sc->fd.bsd_fd,
	    sc->fd.bsd_callback, sc, sc->fd.bsd_mask);
}


void* socks_server_accept_handler(unsigned int assocID, int status,
    unsigned int noOfDestinations, unsigned short noOfInStreams,
    unsigned short noOfOutStreams, int associationSupportsPRSCTP,
    void* ulpDataPtr)
{
	struct socks_server *server = (struct socks_server *)ulpDataPtr;
	struct socks_conn *sc = NULL;
	SCTP_AssociationStatus assocStatus;

	if (sctp_getAssocStatus(assocID, &assocStatus) != SCTP_SUCCESS) {
		syslog(SYSLOG_ERROR, "[socks_server_accept_handler] sctp_getAssocStatus failed\n");
		sctp_shutdown(assocID);
	}

	syslog(SYSLOG_VERBOSE, "[socks_server_accept_handler] new association(%d) from %s/%d\n",
	    assocID, assocStatus.primaryDestinationAddress,
	    assocStatus.destPort);

	/* Find a free connection data. */
	if (connects_idx < connects_max) {
		sc = &connects[connects_idx];
		connects_idx++;
	} else {
		int i;
		for (i = 0; i < connects_idx; i++) {
			if (connects[i].used == 0) {
				break;
			}
		}
		if (i == connects_max) {
			syslog(SYSLOG_ERROR, "[socks_server_accept_handler] no available resource: assocID(%d)", assocID);
			sctp_shutdown(assocID);
			return NULL;
		}
		sc = &connects[i];
		connects_idx = i + 1;
	}
	sc->used = 1;
	sc->server = server;
	sc->fd.type = SOCKS_FD_TYPE_SCTP_SCTPLIB;
	sc->fd.sctp_instance_id = -1;
	sc->fd.sctp_assoc_id = assocID;
	sc->fd.sctp_sndmax = sc->server->fd.sctp_sndmax;

	sc->fwd = NULL;

	socks_start_service(sc);

	syslog(SYSLOG_VERBOSE, "[socks_server_accept_handler] sc(%p) for association(%d)\n",
	    sc, sc->fd.sctp_assoc_id);

	socks_notify("[ACCEPT] id=%p, from_addr=%s, from_port=%u\n",
	    sc, assocStatus.primaryDestinationAddress, assocStatus.destPort);

	return sc;
}


/* Start Socks service. */
void socks_start_service(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_start_service] sc(%p)\n", sc);
	socks_realloc_buf(&sc->service_read_buf, &sc->service_read_size, MAX_RCVBUF);
	socks_realloc_buf(&sc->service_send_buf, &sc->service_send_size, MAX_SNDBUF);

	syslog(SYSLOG_VERBOSE, "fwd(%p)\n", sc->fwd);
	if (sc->fwd != NULL) {
		/* Forwarding... */
		sc->service_state = SERVICE_STATE_PREPARE_SERVICE;
		sc->service_cmd = SOCKS_CMD_CONNECT;
		socks_initialize_proxy(sc);
	} else {
		sc->service_state = SERVICE_STATE_START;

		sc->service_send_idx = 0;
		sc->service_send_sent_idx = 0;

		sc->service_read_idx = 0;
		sc->service_read_checked_idx = 0;
		sc->service_protocol = -1;

		sc->service_auth = SOCKS5_AUTH_NO;
		sc->service_auth_state = AUTH_STATE_START;
		sc->service_auth_start_idx = 0;

		sc->service_cmd_state = CMD_STATE_START;
		sc->service_cmd_start_idx = 0;

		memset(sc->hbuf, 0, sizeof(sc->hbuf));
		memset(sc->sbuf, 0, sizeof(sc->sbuf));
	}
}


/* A callback functoin for BSD socket. */
void socks_service_bsd_handler(int s, short int event, short int *pevent, void *data)
{
	struct socks_conn *sc = (struct socks_conn *)data;

	syslog(SYSLOG_VERBOSE, "[socks_service_bsd_handler] fd(%d), event(%d), sc(%p)\n",
	    s, event, sc);
	if ((event & POLLIN) == POLLIN) {
		socks_service_bsd_read_handler(sc);
	}
	if ((event & POLLOUT) == POLLOUT) {
		socks_service_bsd_send_handler(sc);
	}
	if ((event & POLLHUP) == POLLHUP) {
		/* Connection closed. */
		socks_service_bsd_close_handler(sc);
		return;
	}

#if 0
	if ((sc->service_state != SERVICE_STATE_DO_SERVICE &&
	    sc->service_send_sent_idx < sc->service_send_idx) ||
	    (sc->service_state ==  SERVICE_STATE_DO_SERVICE &&
	    sc->proxy_read_checked_idx < sc->proxy_read_idx)) {
		/* Outstanding data exists. */
		sc->fd.bsd_mask |= POLLOUT;
		*pevent = sc->fd.bsd_mask;
	} else {
		/* No outstanding data. */
		sc->fd.bsd_mask &= ~POLLOUT;
		*pevent = sc->fd.bsd_mask;
	}
#endif
}


void socks_service_bsd_read_handler(struct socks_conn *sc)
{
	ssize_t sz, left;
	int error = 0;

	syslog(SYSLOG_VERBOSE, "[socks_service_bsd_read_handler] sc(%p), fd(%d), idx(%d), checked_idx(%d), size(%d)\n",
	    sc, sc->fd.bsd_fd,
	    sc->service_read_idx, sc->service_read_checked_idx, sc->service_read_size);

	if (sc->service_state < SERVICE_STATE_PREPARE_SERVICE) {
		left = sc->service_read_size - sc->service_read_idx;
	} else {
		if ((sc->service_read_idx == sc->service_read_size) &&
		    (sc->service_read_checked_idx > 0)) {
			sc->service_read_idx = 0;
		}
		if ((sc->service_read_idx > sc->service_read_checked_idx) ||
		    (sc->service_read_idx == 0 && sc->service_read_checked_idx == 0)) {
			left = sc->service_read_size - sc->service_read_idx;
		} else {
			left = sc->service_read_checked_idx - sc->service_read_idx;
		}
	}
	syslog(SYSLOG_VERBOSE, "[socks_service_bsd_read_handler] idx(%d), checked_idx(%d), size(%d), left(%d)\n",
	    sc->service_read_idx, sc->service_read_checked_idx, sc->service_read_size, left);

	if (left <= 0) {
		syslog(SYSLOG_ERROR, "[socks_service_bsd_read_handler] "
		    "no buffer space for additional data.... Sleep a while\n");
		sc->fd.bsd_flags |= FD_FLAGS_DATA_RECEIVED;
		sc->fd.bsd_mask &= ~POLLIN;
		sctp_changeUserCallback(sc->fd.bsd_fd, NULL, NULL,
		    sc->fd.bsd_mask);
		socks_service_read(sc);
		return;
	}

	if (sc->service_state == SERVICE_STATE_DO_SERVICE &&
	    left > sc->service_read_window_size) {
		sz = sc->service_read_window_size;
	} else {
		sz = left;
	}

	sz = recv(sc->fd.bsd_fd,
	    &sc->service_read_buf[sc->service_read_idx], sz, 0);
	if (sz < 0) {
#ifdef WIN32
		error = WSAGetLastError();
#else
		error = errno;
#endif
		syslog(SYSLOG_ERROR, "[socks_service_bsd_read_handler] recv failed: %d\n", error);
	} else {
		syslog(SYSLOG_VERBOSE, "[socks_service_bsd_read_handler] recv: %d\n", sz);
	}

	if (sz > 0) {
		sc->service_read_idx += sz;
		socks_service_read(sc);
	} else if (
#ifdef WIN32
	    sz == 0 || error != WSAEWOULDBLOCK
#else
	    sz == 0 || error != EAGAIN
#endif
	    ) {
		/* Connection closed. */
		socks_service_bsd_close_handler(sc);
	}
}


void socks_service_read_handler(unsigned int assocID, unsigned short streamID,
    unsigned int len, unsigned short streamSN, unsigned int TSN,
    unsigned int protoID, unsigned int unordered, void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	int result = 0;
	ssize_t sz, left;
	unsigned short ssn;
	unsigned int tsn;

	syslog(SYSLOG_VERBOSE, "[socks_service_read_handler] sc(%p), association(%d), idx(%d), checked_idx(%d), size(%d)\n",
	    sc, assocID,
	    sc->service_read_idx, sc->service_read_checked_idx, sc->service_read_size);

	if (sc->service_state < SERVICE_STATE_PREPARE_SERVICE) {
		left = sc->service_read_size - sc->service_read_idx;
	} else {
		if ((sc->service_read_idx == sc->service_read_size) &&
		    (sc->service_read_checked_idx > 0)) {
			sc->service_read_idx = 0;
		}
		if ((sc->service_read_idx > sc->service_read_checked_idx) ||
		    (sc->service_read_idx == 0 && sc->service_read_checked_idx == 0)) {
			left = sc->service_read_size - sc->service_read_idx;
		} else {
			left = sc->service_read_checked_idx - sc->service_read_idx;
		}
	}

	if (left <= 0) {
		syslog(SYSLOG_ERROR, "[socks_service_read_handler] "
		    "no buffer space for additional data.... Sleep a while\n");
		socks_service_read(sc);
		return;
	}

	if (sc->service_state == SERVICE_STATE_DO_SERVICE &&
	    left > sc->service_read_window_size) {
		sz = sc->service_read_window_size;
	} else {
		sz = left;
	}

	result = sctp_receive(assocID, streamID,
	    (unsigned char *)&sc->service_read_buf[sc->service_read_idx],
	    (unsigned int *)&sz, &ssn, &tsn, SCTP_MSG_DEFAULT);
	if (result != SCTP_SUCCESS) {
		syslog(SYSLOG_ERROR, "[socks_service_read_handler] failed\n");
		return;
	}

	syslog(SYSLOG_VERBOSE, "[socks_service_read_handler] sctp_receive: %d\n", sz);
	sc->service_read_idx += sz;

	socks_service_read(sc);
}


void
socks_service_read(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_service_read] sc(%p), state(%d)\n",
	    sc, sc->service_state);
	if (sc->service_state == SERVICE_STATE_DO_SERVICE) {
		socks_proxy_send(sc, NULL, 0);
	} else if (sc->service_state == SERVICE_STATE_PREPARE_SERVICE) {
	} else {
		switch (socks_parse_request(sc))
		{
		case COMPLETE_READ:
			socks_initialize_proxy(sc);
			break;
		case INCOMPLETE_READ:
			break;
		case BAD_READ:
			socks_close_service(sc);
			break;
		}
	}
}


void
socks_service_suspend_read(struct socks_conn *sc)
{
	int result;
	SCTP_AssociationStatus assocStatus;

	syslog(SYSLOG_VERBOSE, "[socks_service_suspend_read] sc=%p, state=%d\n",
	    sc, sc->service_state);

	if (sc->service_state >= SERVICE_STATE_WAIT_CLOSE) {
		return;
	}

	switch (sc->fd.type) {
	case SOCKS_FD_TYPE_TCP_SOCKET:
	case SOCKS_FD_TYPE_SCTP_SOCKET:
		sc->fd.bsd_mask &= ~POLLIN;
		sctp_changeUserCallback(sc->fd.bsd_fd, NULL, NULL,
		    sc->fd.bsd_mask);
		break;
	case SOCKS_FD_TYPE_SCTP_SCTPLIB:
		if ((result = sctp_getAssocStatus(sc->fd.sctp_assoc_id, &assocStatus)) == SCTP_SUCCESS) {
			assocStatus.myRwnd = 0;
			result = sctp_setAssocStatus(sc->fd.sctp_assoc_id, &assocStatus);
			if (result == SCTP_SUCCESS) {
				syslog(SYSLOG_VERBOSE, "[socks_service_suspend_read] sc=%p, myRwnd=%d\n",
				    assocStatus.myRwnd);
			} else {
				syslog(SYSLOG_VERBOSE, "[socks_service_suspend_read] sctp_getAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
				    result, sc, assocStatus.myRwnd);
			}
		} else {
			syslog(SYSLOG_ERROR, "[socks_service_suspend_read] sctp_setAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
			    result, sc, 0);
		}
		break;
	default:
		break;
	}
}


void
socks_service_resume_read(struct socks_conn *sc)
{
	int result;
	SCTP_AssociationStatus assocStatus;

	syslog(SYSLOG_VERBOSE, "[socks_service_resume_read] sc=%p, state=%d\n",
	    sc, sc->service_state);

	if (sc->service_state >= SERVICE_STATE_WAIT_CLOSE) {
		return;
	}

	switch (sc->fd.type) {
	case SOCKS_FD_TYPE_TCP_SOCKET:
	case SOCKS_FD_TYPE_SCTP_SOCKET:
		sc->fd.bsd_mask |= POLLIN;
		sctp_changeUserCallback(sc->fd.bsd_fd, NULL, NULL,
		    sc->fd.bsd_mask);
		if (1 || (sc->fd.bsd_flags & FD_FLAGS_DATA_RECEIVED)) {
			/* Data arraived... */
			socks_service_bsd_read_handler(sc);
		}
		break;
	case SOCKS_FD_TYPE_SCTP_SCTPLIB:
		if ((result = sctp_getAssocStatus(sc->fd.sctp_assoc_id, &assocStatus)) == SCTP_SUCCESS) {
			if (assocStatus.myRwnd == 0) {
				sc->service_read_window_size /= 2;
			}
			assocStatus.myRwnd = sc->service_read_window_size;
			result = sctp_setAssocStatus(sc->fd.sctp_assoc_id, &assocStatus);
			if (result == SCTP_SUCCESS) {
				syslog(SYSLOG_VERBOSE, "[socks_service_resume_read] sc=%p, myRwnd=%d\n",
				    assocStatus.myRwnd);
			} else {
				syslog(SYSLOG_VERBOSE, "[socks_service_resume_read] sctp_getAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
				    result, sc, assocStatus.myRwnd);
			}
		} else {
			syslog(SYSLOG_ERROR, "[socks_service_resume_read] sctp_getAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
			    result, sc, sc->service_read_window_size);
		}
		break;
	default:
		break;
	}
}


int
socks_service_bsd_send_handler(struct socks_conn *sc)
{
	int sz, send_sz, real_send_sz;
	int sent_sz = 0, second_sent_sz = 0;
	int error = 0;

	syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler] sc(%p), fd(%d)\n",
	    sc, sc->fd.bsd_fd);
	if (sc->service_state < SERVICE_STATE_DO_SERVICE) {
		syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#1 idx(%d), sent_idx(%d)\n",
		    sc->service_send_idx, sc->service_send_sent_idx);

		send_sz = sc->service_send_idx - sc->service_send_sent_idx;
		if (send_sz == 0) {
			return 0;
		}

		sz = send(sc->fd.bsd_fd,
		    &sc->service_send_buf[sc->service_send_sent_idx],
		    send_sz, 0);
		if (sz < 0) {
#ifdef WIN32
			error = WSAGetLastError();
#else
			error = errno;
#endif
		}

		if (sz > 0) {
			sent_sz += sz;
		} else if ((sz < 0 &&
#ifdef WIN32
		    (error == WSAENOBUFS || error == WSAEWOULDBLOCK)
#else
		    (error == EWOULDBLOCK || error == EMSGSIZE)
#endif
		    ) || (sz < send_sz)) {
			syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#1 sz=%d, Remaining data exists....\n", sz);

			sc->fd.bsd_mask |= POLLOUT;
			sctp_changeUserCallback(sc->fd.bsd_fd, NULL, NULL,
			    sc->fd.bsd_mask);
			return sz < 0 ? 0 : sz;
		} else if (sz < 0) {
			syslog(SYSLOG_ERROR, "[socks_service_bsd_send_handler]#1 send failed: %d\n", error);
			socks_close_service(sc);
			return -1;
		}

		syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#1 send: %d\n", sz);

		sc->service_send_sent_idx += sz;
	} else if (sc->service_state == SERVICE_STATE_DO_SERVICE) {
		syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#2 size(%d), idx(%d), sent_idx(%d)\n",
		    sc->proxy_read_size, sc->proxy_read_idx, sc->proxy_read_checked_idx);
	
		if ((sc->proxy_read_idx == 0) && (sc->proxy_read_checked_idx) == 0) {
			syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#2 All data transmitted....\n");
			return 0;
		}
		if (sc->proxy_read_idx > sc->proxy_read_checked_idx) {
			send_sz = sc->proxy_read_idx - sc->proxy_read_checked_idx;
		} else {
			send_sz = sc->proxy_read_size - sc->proxy_read_checked_idx;
			if (send_sz == 0) {
				sc->proxy_read_checked_idx = 0;
				send_sz = sc->proxy_read_idx - sc->proxy_read_checked_idx;
			}
		}

		if (send_sz > MAX_DATA_SIZE) {
			real_send_sz = MAX_DATA_SIZE;
		} else {
			real_send_sz = send_sz;
		}
		syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#2 real_send_sz=%d, send_sz=%d, idx=%d, sent_idx=%d\n",
		    real_send_sz, send_sz, sc->proxy_read_idx, sc->proxy_read_checked_idx);

		sz = send(sc->fd.bsd_fd,
		    &sc->proxy_read_buf[sc->proxy_read_checked_idx],
		    real_send_sz, 0);
		if (sz < 0) {
#ifdef WIN32
			error = WSAGetLastError();
#else
			error = errno;
#endif
		}

		syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#2 send: %d\n", sz);

		if (sz > 0) {
			sent_sz += sz;
			sc->proxy_read_checked_idx += sz;
			sc->proxy_read_window_size += sz / 2;
		}

		if (sz == real_send_sz) {
			if ((real_send_sz == send_sz) &&
			    (sc->proxy_read_checked_idx == sc->proxy_read_idx)) {
				syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler] No remaining data....\n");
				sc->proxy_read_idx = 0;
				sc->proxy_read_checked_idx = 0;
				socks_proxy_resume_read(sc);
			} else if ((real_send_sz == send_sz) &&
			    (sc->proxy_read_checked_idx == sc->proxy_read_size)) {
				syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler] Remaining data exists before....\n");
				sc->proxy_read_checked_idx = 0;
				second_sent_sz = socks_service_bsd_send_handler(sc);
				if (second_sent_sz > 0) {
					sent_sz += second_sent_sz;
				}
			} else {
				syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] Remaining data exists....\n");
				second_sent_sz = socks_service_bsd_send_handler(sc);
				if (second_sent_sz > 0) {
					sent_sz += second_sent_sz;
				}
			}
		} else if ((sz < 0 &&
#ifdef WIN32
		    (error == WSAENOBUFS || error == WSAEWOULDBLOCK)
#else
		    (error == EWOULDBLOCK || error == EMSGSIZE)
#endif
		    ) || (sz < real_send_sz)) {
			syslog(SYSLOG_VERBOSE, "[socks_service_bsd_send_handler]#2 sz=%d, Remaining data exists....\n", sz);

			sc->fd.bsd_mask |= POLLOUT;
			sctp_changeUserCallback(sc->fd.bsd_fd, NULL, NULL,
			    sc->fd.bsd_mask);
			socks_proxy_suspend_read(sc);
			return sz < 0 ? 0 : sz;
		} else if (sz < 0) {
			syslog(SYSLOG_ERROR, "[socks_service_bsd_send_handler] send failed: %d\n", error);
			socks_close_service(sc);
			socks_close_proxy(sc);
			return -1;
		}

		if (sc->proxy_state >= SERVICE_STATE_FIN && sc->proxy_read_idx == 0) {
			socks_close_service(sc);
		}
	}

	return sent_sz;
}


int
socks_service_send_handler(struct socks_conn *sc)
{
	int send_sz = 0, real_send_sz = 0;
	int sent_sz = 0, second_sent_sz = 0;
	int result = 0;

	if (sc->service_state < SERVICE_STATE_DO_SERVICE) {
		syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#1 idx(%d), sent_idx(%d)\n",
		    sc->service_send_idx, sc->service_send_sent_idx);

		send_sz = sc->service_send_idx - sc->service_send_sent_idx;
		if (send_sz == 0) {
			return 0;
		}
		if (send_sz > SCTP_MAXIMUM_DATA_LENGTH) {
			real_send_sz = SCTP_MAXIMUM_DATA_LENGTH;
		} else {
			real_send_sz = send_sz;
		}

		result = sctp_send(sc->fd.sctp_assoc_id, 0,
		    (unsigned char *)&sc->service_send_buf[sc->service_send_sent_idx],
		    real_send_sz, 0, SCTP_USE_PRIMARY, SCTP_NO_CONTEXT,
		    SCTP_INFINITE_LIFETIME, 0, SCTP_BUNDLING_DISABLED);
		if (result == SCTP_SUCCESS) {
			sent_sz += real_send_sz;
			sc->service_send_sent_idx += real_send_sz;

			if (real_send_sz == send_sz) {
				syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#1 No remaining data....\n");
			} else {
				syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#1 Remaining data exists....\n");
				second_sent_sz = socks_service_send_handler(sc);
				if (second_sent_sz > 0) {
					sent_sz += second_sent_sz;
				}
			}
		} else if (result == SCTP_QUEUE_EXCEEDED) {
			syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#1 sctp_send return SCTP_QUEUE_EXCEEDED\n");
			sc->fd.sctp_flags |= FD_FLAGS_EXCEED_SEND;
			return 0;
		} else {
			syslog(SYSLOG_ERROR, "[socks_service_send_handler] sctp_send failed: %d\n", result);
			return -1;
		}
	} else {
		syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 association(%d), size(%d), idx(%d), sent_idx(%d)\n",
		    sc->fd.sctp_assoc_id,
		    sc->proxy_read_size, sc->proxy_read_idx, sc->proxy_read_checked_idx);

		if ((sc->proxy_read_idx == 0) && (sc->proxy_read_checked_idx) == 0) {
			syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 Already all data transmitted....\n");
			return 0;
		}
		if (sc->proxy_read_idx > sc->proxy_read_checked_idx) {
			send_sz = sc->proxy_read_idx - sc->proxy_read_checked_idx;
		} else {
			send_sz = sc->proxy_read_size - sc->proxy_read_checked_idx;
			if (send_sz == 0) {
				sc->proxy_read_checked_idx = 0;
				send_sz = sc->proxy_read_idx - sc->proxy_read_checked_idx;
			}
		}

		if (send_sz > SCTP_MAXIMUM_DATA_LENGTH) {
			real_send_sz = SCTP_MAXIMUM_DATA_LENGTH;
		} else {
			real_send_sz = send_sz;
		}

		syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 real_send_sz(%d), send_sz(%d), idx(%d), checked_idx(%d)\n",
		    real_send_sz, send_sz, sc->proxy_read_idx, sc->proxy_read_checked_idx);

		result = sctp_send(sc->fd.sctp_assoc_id, 0,
		    (unsigned char *)&sc->proxy_read_buf[sc->proxy_read_checked_idx],
		    real_send_sz, 0, SCTP_USE_PRIMARY, SCTP_NO_CONTEXT,
		    SCTP_INFINITE_LIFETIME, 0, SCTP_BUNDLING_DISABLED);
		if (result == SCTP_SUCCESS) {
			sent_sz += real_send_sz;
			sc->proxy_read_checked_idx += real_send_sz;
			sc->proxy_read_window_size += real_send_sz / 2;

			if ((real_send_sz == send_sz) &&
			    (sc->proxy_read_checked_idx == sc->proxy_read_idx)) {
				syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 No remaining data....\n");
				sc->proxy_read_idx = 0;
				sc->proxy_read_checked_idx = 0;
				socks_proxy_resume_read(sc);
			} else if ((real_send_sz == send_sz) &&
			    (sc->service_read_checked_idx == sc->service_read_size)) {
				syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 Remaining data exists before....\n");
				sc->proxy_read_checked_idx = 0;
				second_sent_sz = socks_service_send_handler(sc);
				if (second_sent_sz > 0) {
					sent_sz += second_sent_sz;
				}
			} else {
				syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 Remaining data exists....\n");
				second_sent_sz = socks_service_send_handler(sc);
				if (second_sent_sz > 0) {
					sent_sz += second_sent_sz;
				}
			}
		} else if (result == SCTP_QUEUE_EXCEEDED) {
			syslog(SYSLOG_VERBOSE, "[socks_service_send_handler]#2 sctp_send return SCTP_QUEUE_EXCEEDED\n");
			sc->fd.sctp_flags |= FD_FLAGS_EXCEED_SEND;
			socks_proxy_suspend_read(sc);
			return 0;
		} else {
			syslog(SYSLOG_ERROR, "[socks_service_send_handler]#2 sctp_send failed: %d\n", result);
			socks_close_service(sc);
			socks_close_proxy(sc);
			return -1;
		}

		if (sc->proxy_state >= SERVICE_STATE_FIN && sc->proxy_read_idx == 0) {
			socks_close_service(sc);
		}
	}

	return sent_sz;
}


int
socks_service_send(struct socks_conn *sc, char *str, size_t sz)
{
	int result = 0;

	syslog(SYSLOG_VERBOSE, "[socks_service_send] sc(%p) str(%p) sz(%u)\n", sc, str, sz);
	if (sz > 0) {
		socks_realloc_buf(&sc->service_send_buf, &sc->service_send_size,
		    sc->service_send_idx + sz);
		memmove(&sc->service_send_buf[sc->service_send_idx], str, sz);
		sc->service_send_idx += sz;
	}

	if (sc->fd.type == SOCKS_FD_TYPE_TCP_SOCKET ||
	    sc->fd.type == SOCKS_FD_TYPE_SCTP_SOCKET) {
		syslog(SYSLOG_VERBOSE, "[socks_service_send]#1\n");
		result = socks_service_bsd_send_handler(sc);
	} else {
		syslog(SYSLOG_VERBOSE, "[socks_service_send]#2\n");
		result = socks_service_send_handler(sc);
	}

	return result;
}


void
socks_service_queue_status_handler(unsigned int assocID,
    int queueType, int queueId, int queueLen, void *ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_service_queue_status_handler] sc(%p), association(%d), queueType(%d), queueId(%d), queueLen(%d)\n",
	    sc, assocID, queueType, queueId, queueLen);

	if (queueType != SCTP_SEND_QUEUE) {
		return;
	}
	if (sc->fd.sctp_flags & FD_FLAGS_EXCEED_SEND) {
		sc->proxy_fd.sctp_flags &= ~FD_FLAGS_EXCEED_SEND;
		socks_service_send_handler(sc);
	}
}


void socks_service_bsd_close_handler(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_service_bsd_close_handler] sc(%p), fd(%d)\n",
	    sc, sc->fd.bsd_fd);

#ifdef WIN32
	if (sc->fd.bsd_fd == INVALID_SOCKET) {
#else
	if (sc->fd.bsd_fd == -1) {
#endif
		return;
	}
	socks_notify("[CLOSED] id=%p\n", sc);

	sctp_unregisterUserCallback((int)sc->fd.bsd_fd);
#ifdef WIN32
	sc->fd.bsd_fd = INVALID_SOCKET;
	closesocket(sc->fd.bsd_fd);
#else
	close(sc->fd.bsd_fd);
	sc->fd.bsd_fd = -1;
#endif

	sc->service_state = SERVICE_STATE_CLOSE;

	switch (sc->proxy_state) {
	case PROXY_STATE_FREE:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		socks_close_proxy(sc);
		break;
	case PROXY_STATE_DO_SERVICE:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		if (sc->service_read_idx == 0) {
			/* No remaining data. Close proxy side. */
			socks_close_proxy(sc);
		}
		break;
	case PROXY_STATE_FIN:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		/* Proxy side not closed completely. Wait. */
		break;
	case PROXY_STATE_WAIT_CLOSE:
		socks_free_proxy(sc);
		socks_free_service(sc);
		break;
	case PROXY_STATE_CLOSE:
		socks_free_service(sc);
		break;
	default:
		break;
	}
}


void
socks_service_lost_handler(unsigned int assocID, unsigned short eventType,
    void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_service_lost_handler] sc(%p), association(%d), type(%d\n",
	    sc, assocID, eventType);

	socks_notify("[ABORTED] id=%p\n", sc);

	sctp_deleteAssociation(assocID);
	sctp_unregisterInstance(sc->fd.sctp_instance_id);

	sc->fd.sctp_instance_id = -1;
	sc->fd.sctp_assoc_id = -1;

	sc->service_state = SERVICE_STATE_CLOSE;

	switch (sc->proxy_state) {
	case PROXY_STATE_FREE:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		break;
	case PROXY_STATE_DO_SERVICE:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		if (sc->service_read_idx == 0) {
			/* No remaining data. Close proxy side. */
			socks_close_proxy(sc);
		}
		break;
	case PROXY_STATE_FIN:
		/* Proxy side not closed completely. Wait. */
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		break;
	case PROXY_STATE_WAIT_CLOSE:
		socks_free_proxy(sc);
		socks_free_service(sc);
		break;
	case PROXY_STATE_CLOSE:
		socks_free_service(sc);
		break;
	default:
		break;
	}
}


void
socks_service_shutdown_handler(unsigned int assocID, void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_service_shutdown_handler] sc(%p), association(%d)\n",
	    sc, assocID);

	socks_notify("[CLOSING] id=%p\n", sc);

	sc->service_state = SERVICE_STATE_FIN;

	switch (sc->proxy_state) {
	case PROXY_STATE_FREE:
		break;
	case PROXY_STATE_DO_SERVICE:
		if (sc->service_read_idx == 0) {
			/* No remaining data. Close proxy side. */
			socks_close_proxy(sc);
		}
		break;
	case PROXY_STATE_FIN:
		/* Proxy side not closed. Wait for close. */
		break;
	case PROXY_STATE_WAIT_CLOSE:
		socks_free_proxy(sc);
		break;
	case PROXY_STATE_CLOSE:
		break;
	default:
		break;
	}
}


void
socks_service_close_handler(unsigned int assocID, void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_service_close_handler] sc(%p), association(%d)\n",
	    sc, assocID);

	socks_notify("[CLOSED] id=%p\n", sc);

	sctp_deleteAssociation(assocID);
	sctp_unregisterInstance(sc->fd.sctp_instance_id);

	sc->service_state = SERVICE_STATE_CLOSE;

	switch (sc->proxy_state) {
	case PROXY_STATE_FREE:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		break;
	case PROXY_STATE_DO_SERVICE:
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		if (sc->service_read_idx == 0) {
			/* No remaining data. Close proxy side. */
			socks_close_proxy(sc);
		}
		break;
	case PROXY_STATE_FIN:
		/* Proxy side not closed. Wait for close. */
		sc->service_state = SERVICE_STATE_WAIT_CLOSE;
		break;
	case PROXY_STATE_WAIT_CLOSE:
		socks_free_proxy(sc);
		socks_free_service(sc);
		break;
	case PROXY_STATE_CLOSE:
		socks_free_service(sc);
		break;
	default:
		break;
	}
}


void
socks_service_asconf_handler(unsigned int assocID, unsigned short type,
    unsigned char address[SCTP_MAX_IP_LEN], int result, void *ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_service_asconf_handler] sc(%p), association(%d) "
	    "type(%d), %s, result(%d)\n", sc, assocID, address, result);

	switch (type) {
	case SCTP_ASCONF_ADD:
		socks_notify("[SCTP:LOCAL] ADD %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_ADD_SUCCESS:
		socks_notify("[SCTP:LOCAL] ADD SUCCESS %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_ADD_FAIL:
		socks_notify("[SCTP:LOCAL] ADD FAILED %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_DEL:
		socks_notify("[SCTP:LOCAL] DEL %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_DEL_SUCCESS:
		socks_notify("[SCTP:LOCAL] DEL SUCCESS %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_DEL_FAIL:
		socks_notify("[SCTP:LOCAL] DEL FAILED %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_SET_PRIMARY:
		socks_notify("[SCTP:LOCAL] SET PRIMARY %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_SET_PRIMARY_SUCCESS:
		socks_notify("[SCTP:LOCAL] SET PRIMARY SUCCESS %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_SET_PRIMARY_FAIL:
		socks_notify("[SCTP:LOCAL] SET PRIMARY FAILED %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	default:
		break;
	}
}


void
socks_service_network_status_handler(unsigned int assocID, short destKey,
    unsigned char destStr[SCTP_MAX_IP_LEN], unsigned short status, void *ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_service_network_status_handler] sc=%p, assoc=%u dest=%d, destStr=%s, status=%u\n",
	    sc, assocID, destKey, destStr, status);

	switch (status) {
	case SCTP_PATH_ADDED:
		socks_notify("[SCTP:PEER] ADD %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_REMOVED:
		socks_notify("[SCTP:PEER] DEL %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_ACTIVE:
		socks_notify("[SCTP:PEER] ACTIVE %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_INACTIVE:
		socks_notify("[SCTP:PEER] INACTIVE %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_CONFIRMED:
		socks_notify("[SCTP:PEER] CONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_UNCONFIRMED:
		socks_notify("[SCTP:PEER] UNCONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_SRC_CONFIRMED:
		socks_notify("[SCTP:PEER] SRC_CONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_SRC_UNCONFIRMED:
		socks_notify("[SCTP:PEER] SRC_UNCONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_PRIMARY:
		socks_notify("[SCTP:PEER] PRIMARY %s, assoc=%u\n", destStr, assocID);
		break;
	default:
		break;
	}
}


/* Close Socks service. */
int
socks_close_service(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_close_service] sc(%p), sc->service_state(%d)\n",
	    sc, sc != NULL ? sc->service_state : -1);

	if (sc->service_state == SERVICE_STATE_FREE) {
		return 0;
	}

	if (sc->fd.type == SOCKS_FD_TYPE_SCTP_SOCKET ||
	    sc->fd.type == SOCKS_FD_TYPE_TCP_SOCKET) {
		syslog(SYSLOG_VERBOSE, "[socks_close_service] sc(%p), fd(%d)\n",
		    sc, sc->fd.bsd_fd);

		/* For BSD socket, i.e. TCP */
#ifdef WIN32
		if (sc->fd.bsd_fd != INVALID_SOCKET) {
#else
		if (sc->fd.bsd_fd != -1) {
#endif
			socks_notify("[CLOSED] id=%p\n", sc);

			sctp_unregisterUserCallback((int)sc->fd.bsd_fd);
#ifdef WIN32
			closesocket(sc->fd.bsd_fd);
			sc->fd.bsd_fd = INVALID_SOCKET;
#else
			close(sc->fd.bsd_fd);
			sc->fd.bsd_fd = -1;
#endif
			if (sc->proxy_state == PROXY_STATE_FREE ||
			    sc->proxy_state == PROXY_STATE_CLOSE) {
				socks_free_service(sc);
			} else {
				sc->service_state = SERVICE_STATE_WAIT_CLOSE;
			}
		}
	} else {
		syslog(SYSLOG_VERBOSE, "[socks_close_service] sc(%p), association(%d)\n",
		    sc, sc->fd.sctp_assoc_id);
		/* For sctplib. */
		if (sc->fd.sctp_assoc_id != -1) {
			socks_notify("[CLOSING] id=%p\n", sc);
			sctp_shutdown(sc->fd.sctp_assoc_id);
			sc->service_state = SERVICE_STATE_FIN;
		}
	}
	return 0;
}


void
socks_free_service(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_free_service] sc(%p), service_read_buf(%p), service_send_buf(%p)\n",
	    sc, sc->service_read_buf, sc->service_send_buf);

	sc->service_state = SERVICE_STATE_FREE;
	sc->used = 0;

	if (sc->service_read_buf != NULL) {
		free(sc->service_read_buf);
		sc->service_read_buf = NULL;
	}
	if (sc->service_send_buf != NULL) {
		free(sc->service_send_buf);
		sc->service_send_buf = NULL;
	}
}


int
socks_parse_request(struct socks_conn *sc)
{
	int status = 0;
	/* XXX
	 * This function assumes that service_read_idx > service_read_checked_idx.
	 * That is, receiver buffer isn't split.
	 */

	syslog(SYSLOG_VERBOSE, "[socks_parse_request] sc(%p), state(%d)\n",
	    sc, sc->service_state);
	if (sc->service_state == SERVICE_STATE_START) {
		/* Read SOCKS version. */
		if (sc->service_read_idx - sc->service_read_checked_idx >= 1) {
			sc->service_protocol = sc->service_read_buf[0];
			if (sc->service_protocol != SOCKS_VERSION_4 &&
			    sc->service_protocol != SOCKS_VERSION_5) {
				/* Unknonw vesion....*/
				return BAD_READ;
			}
			sc->service_read_checked_idx += 1;
			switch (sc->service_protocol) {
			case SOCKS_VERSION_4:
				sc->service_state = SERVICE_STATE_READ_CMD_REQUEST;
				break;
			case SOCKS_VERSION_5:
				sc->service_state = SERVICE_STATE_READ_AUTH_REQUEST;
				break;
			}
		}
	}

	if (sc->service_state == SERVICE_STATE_READ_AUTH_REQUEST) {
		status = socks5_parse_auth(sc);
		if (status != COMPLETE_READ) {
			return status;
		}
		sc->service_state = SERVICE_STATE_DO_AUTH;
	}

	if (sc->service_state == SERVICE_STATE_DO_AUTH) {
		status = socks5_do_auth(sc);
		if (status != COMPLETE_READ)
		{
			return status;
		}
		sc->service_state = SERVICE_STATE_READ_CMD_REQUEST;
	}

	if (sc->service_state == SERVICE_STATE_READ_CMD_REQUEST) {
		switch (sc->service_protocol)
		{
		case 4:
			status = socks4_parse_cmd(sc);
			break;
		case 5:
			status = socks5_parse_cmd(sc);
			break;
		default:
			return BAD_READ;
		}
		if (status == COMPLETE_READ) {
			sc->service_state = SERVICE_STATE_PREPARE_SERVICE;
			return COMPLETE_READ;
		}
				
	}

	return INCOMPLETE_READ;
}


int
socks5_parse_auth(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks5_parse_auth] sc(%p)\n");

	while (sc->service_read_checked_idx < sc->service_read_idx) {
		if (sc->service_auth_start_idx == 0) {
			sc->service_auth_start_idx = sc->service_read_checked_idx;
			sc->service_read_checked_idx++;
			continue;
		}

		switch (sc->service_read_buf[sc->service_read_checked_idx]) {
		case SOCKS5_AUTH_NOTHING:
			if (sc->service_auth == SOCKS5_AUTH_NO) {
				sc->service_auth = SOCKS5_AUTH_NOTHING;
			}
			break;
		case SOCKS5_AUTH_PASSWORD:
			if (sc->service_auth != SOCKS5_AUTH_GSSAPI) {
				sc->service_auth = SOCKS5_AUTH_PASSWORD;
			}
			break;
		case SOCKS5_AUTH_GSSAPI:
		default:
			return BAD_READ;
		}
		sc->service_read_checked_idx++;

		if (sc->service_read_checked_idx - sc->service_auth_start_idx ==
		    sc->service_read_buf[sc->service_auth_start_idx] + 1) {
			socks5_response_auth(sc);
			if (sc->service_auth != SOCKS5_AUTH_NO) {
				return COMPLETE_READ;
			} else {
				return BAD_READ;
			}
		}
	}

	return INCOMPLETE_READ;
}


void
socks5_response_auth(struct socks_conn *sc)
{
	char response[2];

	syslog(SYSLOG_VERBOSE, "[socks5_response_auth] sc(%p)\n");

	memset(response, 0, sizeof(response));
	response[0] = SOCKS_VERSION_5;
	response[1] = sc->service_auth;
	socks_service_send(sc, response, sizeof(response));
}


int
socks5_do_auth(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks5_do_auth] sc(%p)\n");

	switch (sc->service_auth) {
	case SOCKS5_AUTH_NOTHING:
		return COMPLETE_READ;
	case SOCKS5_AUTH_PASSWORD:
		return socks5_do_auth_password(sc);
	}
	return INCOMPLETE_READ;
}


int
socks5_do_auth_password(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks5_do_auth_password] sc(%p)\n");

	while (sc->service_read_checked_idx < sc->service_read_idx) {
		switch (sc->service_auth_state) {
		case AUTH_STATE_START:
			sc->service_auth_start_idx = sc->service_read_checked_idx;
			if (sc->service_read_buf[sc->service_read_checked_idx] !=
			    SOCKS5_AUTH_PASSWORD_VERSION) {
				return BAD_READ;
			}
			sc->service_read_checked_idx++;
			sc->service_auth_state = AUTH_STATE_PASSWORD_READ_UNAME;
			break;
		case AUTH_STATE_PASSWORD_READ_UNAME:
			if (sc->service_read_idx - sc->service_read_checked_idx >=
			    (size_t)sc->service_read_buf[sc->service_read_checked_idx] + 1) {
				sc->service_read_checked_idx +=
				    sc->service_read_buf[sc->service_read_checked_idx] + 1;
				sc->service_auth_state = AUTH_STATE_PASSWORD_READ_PASS;
			} else {
				return INCOMPLETE_READ;
			}
		case AUTH_STATE_PASSWORD_READ_PASS:
			if (sc->service_read_idx - sc->service_read_checked_idx >=
			    (size_t)sc->service_read_buf[sc->service_read_checked_idx] + 1) {
				size_t sz_uname;
				size_t sz_pass;

				sc->service_read_checked_idx +=
				    sc->service_read_buf[sc->service_read_checked_idx] + 1;
				sc->service_auth_state = AUTH_STATE_END;

				sz_uname = sc->service_read_buf[sc->service_auth_start_idx + 1];
				sz_pass = sc->service_read_buf[sc->service_auth_start_idx + sz_uname + 1];
				if (socks5_check_auth_password(
				    &sc->service_read_buf[sc->service_auth_start_idx + 2], sz_uname,
				    &sc->service_read_buf[sc->service_auth_start_idx + sz_uname + 3], sz_pass) < 0) {
					socks5_response_auth_password(sc, SOCKS5_AUTH_PASSWORD_NG);
					return BAD_READ;
				} else {
					memset(sc->username, 0, sizeof(sc->username));
					memcpy(sc->username,
					    &sc->service_read_buf[sc->service_auth_start_idx + 2],
					    MIN(sizeof(sc->username) - 1, sz_uname));
					memset(sc->password, 0, sizeof(sc->password));
					memcpy(sc->password,
					    &sc->service_read_buf[sc->service_auth_start_idx + sz_uname + 3],
					    MIN(sizeof(sc->username) - 1, sz_pass));

					socks5_response_auth_password(sc, SOCKS5_AUTH_PASSWORD_OK);
					return COMPLETE_READ;
				}
			} else
			{
				return INCOMPLETE_READ;
			}
		default:
			break;
		}
	}
	return INCOMPLETE_READ;
}


int
socks5_check_auth_password(char *username, size_t sz_username,
    char *pass, size_t sz_pass)
{
	syslog(SYSLOG_VERBOSE, "[socks5_check_auth_password] sc(%p)\n");

	return 0;
}


void
socks5_response_auth_password(struct socks_conn *sc, unsigned char status)
{
	char response[2];

	syslog(SYSLOG_VERBOSE, "[socks5_response_auth_password] sc(%p)\n", sc);

	memset(response, 0, sizeof(response));
	response[0] = SOCKS_VERSION_5;
	response[1] = status;
	socks_service_send(sc, response, sizeof(response));
}


int
socks4_parse_cmd(struct socks_conn *sc)
{
	size_t sz = 0;

	syslog(SYSLOG_VERBOSE, "[socks4_parse_cmd] sc(%p)\n", sc);

	while (sc->service_read_checked_idx < sc->service_read_idx) {
		switch (sc->service_cmd_state) {
		case CMD_STATE_START:
			sc->service_cmd_start_idx = sc->service_read_checked_idx;
			sc->service_cmd_state = CMD_STATE_READ_CMD_REQUEST;
			break;
		case CMD_STATE_READ_CMD_REQUEST:
			sc->service_cmd = sc->service_read_buf[sc->service_read_checked_idx];
			if (sc->service_cmd != SOCKS_CMD_CONNECT) {
				/* Currently, only CONNECT request supported. */
				return BAD_READ;
			}
			sc->service_read_checked_idx++;
			sc->service_cmd_state = CMD_STATE_READ_PORT;
			break;
		case CMD_STATE_READ_PORT:
			if (sc->service_read_idx - sc->service_read_checked_idx < 2) {
				return INCOMPLETE_READ;
			}
#ifdef WIN32
			_snprintf(sc->sbuf, sizeof(sc->sbuf), "%d",
			    ntohs(*((unsigned short *)&sc->service_read_buf[sc->service_read_checked_idx])));
#else
			snprintf(sc->sbuf, sizeof(sc->sbuf), "%d",
			    ntohs(*((unsigned short *)&sc->service_read_buf[sc->service_read_checked_idx])));
#endif
			sc->service_read_checked_idx += 2;
			sc->service_cmd_state = CMD_STATE_READ_ADDR;
			break;
		case CMD_STATE_READ_ADDR:
			sz = socks4_parse_addr(sc);
			if (sz == -1) {
				return BAD_READ;
			} else if (sz == 0) {
				return INCOMPLETE_READ;
			}
			sc->service_read_checked_idx += sz;
			sc->service_cmd_state = CMD_STATE_READ_USER;
			break;
		case CMD_STATE_READ_USER:
			if (sc->service_read_buf[sc->service_read_checked_idx] != '\0') {
				sc->service_read_checked_idx++;
				continue;
			}
			sc->service_read_checked_idx++; /* Skip NULL */

			strncpy(sc->username,
			    (char *)&sc->service_read_buf[sc->service_cmd_start_idx + 7],
			    sizeof(sc->username));
			sc->service_cmd_state = CMD_STATE_END;
			return COMPLETE_READ;
		}
	}
	return INCOMPLETE_READ;
}


int
socks4_parse_addr(struct socks_conn *sc)
{
	size_t sz = 0;

	syslog(SYSLOG_VERBOSE, "[socks4_parse_addr] sc(%p)\n", sc);

	sz = SOCKS4_ADDR_SIZE;
	if (sc->service_read_idx - sc->service_read_checked_idx < sz) {
		return 0;
	} else {
		struct sockaddr_in sin;
		int error;

		memset(&sin, 0, sizeof(sin));
		sin.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
		sin.sin_len = sizeof(sin);
#endif
		memmove(&sin.sin_addr,
		    &sc->service_read_buf[sc->service_read_checked_idx],
		    sizeof(struct in_addr));
		error = getnameinfo((struct sockaddr *)&sin, sizeof(sin),
		    sc->hbuf, sizeof(sc->hbuf), NULL, 0,
		    NI_NUMERICHOST);
		if (error < 0) {
			return -1;
		}
	}
	return sz;
}


void
socks4_response_cmd(struct socks_conn *sc, unsigned char result)
{
	char response[8];
	size_t idx = 0;

	syslog(SYSLOG_VERBOSE, "[socks4_response_cmd] sc(%p)\n", sc);

	memset(response, 0, sizeof(response));

	response[idx++] = 0x00;
	response[idx++] = result;

	if (sc->connect_res->ai_family == AF_INET) {
		struct sockaddr_in *sin =
		    (struct sockaddr_in *)sc->connect_res->ai_addr;

		memmove(&response[idx], &sin->sin_port, sizeof(sin->sin_port));
		idx += sizeof(sin->sin_port);

		memmove(&response[idx], &sin->sin_addr, sizeof(sin->sin_addr));
		idx += sizeof(sin->sin_addr);
	} else {
		idx += 6;
	}
	socks_service_send(sc, response, idx);
}


int
socks5_parse_cmd(struct socks_conn *sc)
{
	size_t sz = 0;

	syslog(SYSLOG_VERBOSE, "[socks5_parse_cmd] sc(%p)\n", sc);

	while (sc->service_read_checked_idx < sc->service_read_idx) {
		switch (sc->service_cmd_state) {
		case CMD_STATE_START:
			if (sc->service_read_buf[sc->service_read_checked_idx] !=
			    SOCKS_VERSION_5) {
				return BAD_READ;
			}

			sc->service_read_checked_idx++;
			sc->service_cmd_state = CMD_STATE_READ_CMD_REQUEST;
			break;
		case CMD_STATE_READ_CMD_REQUEST:
			sc->service_cmd = sc->service_read_buf[sc->service_read_checked_idx];
			switch (sc->service_cmd) {
			case SOCKS_CMD_CONNECT:
			case SOCKS_CMD_BIND:
			case SOCKS5_CMD_UDP_ASSOCIATE:
				break;
			default:
				return BAD_READ;
			}

			sc->service_read_checked_idx++;
			sc->service_cmd_state = CMD_STATE_READ_RSV;
			break;
		case CMD_STATE_READ_RSV:
			sc->service_read_checked_idx++;
			sc->service_cmd_state = CMD_STATE_READ_ADDR;
			break;
		case CMD_STATE_READ_ADDR:
			sz = socks5_parse_addr(sc);
			if (sz < 0) {
				return BAD_READ;
			} else if (sz == 0) {
				return INCOMPLETE_READ;
			}
			sc->service_read_checked_idx += sz;
			sc->service_cmd_state = CMD_STATE_READ_PORT;
			break;
		case CMD_STATE_READ_PORT:
			if (sc->service_read_idx - sc->service_read_checked_idx < 2) {
				return INCOMPLETE_READ;
			}
#ifdef WIN32
			_snprintf(sc->sbuf, sizeof(sc->sbuf), "%d",
			    ntohs(*((unsigned short *)&sc->service_read_buf[sc->service_read_checked_idx])));
#else
			snprintf(sc->sbuf, sizeof(sc->sbuf), "%d",
			    ntohs(*((unsigned short *)&sc->service_read_buf[sc->service_read_checked_idx])));
#endif
			sc->service_read_checked_idx += 2;
			sc->service_cmd_state = CMD_STATE_END;
			return COMPLETE_READ;
		}
	}

	return INCOMPLETE_READ;
}


int
socks5_parse_addr(struct socks_conn *sc)
{
	size_t sz = 1;
	size_t hostname_sz = 0;

	syslog(SYSLOG_VERBOSE, "[socks5_parse_addr] sc(%p)\n", sc);

	switch (sc->service_read_buf[sc->service_read_checked_idx]) {
	case SOCKS5_ADDR_IPV4:
		syslog(SYSLOG_VERBOSE, "[socks5_parse_addr] SOCKS5_ADDR_IPV4\n");

		sz += SOCKS5_ADDR_IPV4_SIZE;
		if (sc->service_read_idx - sc->service_read_checked_idx < sz) {
			return 0;
		} else {
			struct sockaddr_in sin;
			int error;

			memset(&sin, 0, sizeof(sin));
			sin.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
			sin.sin_len = sizeof(sin);
#endif
			memmove(&sin.sin_addr,
			    &sc->service_read_buf[sc->service_read_checked_idx + 1],
			    sizeof(struct in_addr));
			error = getnameinfo((struct sockaddr *)&sin, sizeof(sin),
			    sc->hbuf, sizeof(sc->hbuf), NULL, 0,
			    NI_NUMERICHOST);
			if (error < 0)
			{
				return -1;
			}
		}
		break;
	case SOCKS5_ADDR_IPV6:
		syslog(SYSLOG_VERBOSE, "[socks5_parse_addr] SOCKS5_ADDR_IPV6\n");

		sz += SOCKS5_ADDR_IPV6_SIZE;
		if (sc->service_read_idx - sc->service_read_checked_idx < sz) {
			return 0;
		} else {
			struct sockaddr_in6 sin6;
			int error;

			memset(&sin6, 0, sizeof(sin6));
			sin6.sin6_family = AF_INET6;
#ifdef HAVE_SIN_LEN
			sin6.sin6_len = sizeof(sin6);
#endif
			memmove(&sin6.sin6_addr,
			    &sc->service_read_buf[sc->service_read_checked_idx + 1],
			    sizeof(struct in6_addr));
			error = getnameinfo((struct sockaddr *)&sin6, sizeof(sin6),
			    sc->hbuf, sizeof(sc->hbuf), NULL, 0,
			    NI_NUMERICHOST);
			if (error < 0)
			{
				return -1;
			}
		}
		break;
	case SOCKS5_ADDR_HOSTNAME:
		syslog(SYSLOG_VERBOSE, "[socks5_parse_addr] SOCKS5_ADDR_HOSTNAME\n");

		sz += 1;
		if (sc->service_read_idx - sc->service_read_checked_idx < sz) {
			return 0;
		}
		hostname_sz = sc->service_read_buf[sc->service_read_checked_idx + 1];
		if (hostname_sz + 1 > sizeof(sc->hbuf)) {
			syslog(SYSLOG_ERROR, "[socks5_parse_addr] domainname too big\n");
			return -1;
		}
		if (sc->service_read_idx - sc->service_read_checked_idx < sz + hostname_sz) {
			return 0;
		}
		memset(sc->hbuf, 0, sizeof(sc->hbuf));
		memmove(sc->hbuf,
		    (char *)&sc->service_read_buf[sc->service_read_checked_idx + 2],
		    hostname_sz);
		sz += hostname_sz;
		break;
	default:
		syslog(SYSLOG_ERROR, "[socks5_parse_addr] Invalid ADDR\n");
		return -1;
	}
	return sz;
}


void
socks5_response_cmd(struct socks_conn *sc, unsigned char code)
{
	size_t idx = 0;
	size_t sz = 0;
	char response[256];
	struct addrinfo *res;

	syslog(SYSLOG_VERBOSE, "[socks5_response_cmd] sc(%p)\n", sc);

	memset(response, 0, sizeof(response));
	response[idx++] = SOCKS_VERSION_5;
	response[idx++] = code;
	idx++; /* RESERVED */

	sz = sizeof(response) - idx;
	for (res = sc->bind_res; res; res = res->ai_next) {
		if (res->ai_family == sc->connect_res->ai_family)
			break;
	}
	if (socks5_pack_addr(res ? res : sc->bind_res, &response[idx], &sz) < 0) {
		return;
	}
	idx += sz;
	socks_service_send(sc, response, idx);
}


int
socks5_pack_addr(struct addrinfo *res, char *buf, size_t *sz_bufP)
{
	size_t idx = 0;

	if (res->ai_family == AF_INET) {
		struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr;

		if (*sz_bufP < idx + 1) {
			return -1;
		}
		buf[idx++] = SOCKS5_ADDR_IPV4;

		if (*sz_bufP < idx + sizeof(struct in_addr)) {
			return -1;
		}
		memmove(&buf[idx], &sin->sin_addr, sizeof(struct in_addr));
		idx += sizeof(struct in_addr);

		if (*sz_bufP < idx + sizeof(unsigned short)) {
			return -1;
		}
		memmove(&buf[idx], &sin->sin_port, sizeof(unsigned short));
		idx += sizeof(unsigned short);

		*sz_bufP = idx;
	} else if (res->ai_family == AF_INET6) {
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;

		if (*sz_bufP < idx + 1) {
			return -1;
		}
		buf[idx++] = SOCKS5_ADDR_IPV6;

		if (*sz_bufP < idx + sizeof(struct in6_addr)) {
			return -1;
		}
		memmove(&buf[idx], &sin6->sin6_addr, sizeof(struct in6_addr));
		idx += sizeof(struct in6_addr);


		if (*sz_bufP < idx + sizeof(unsigned short)) {
			return -1;
		}
		memmove(&buf[idx], &sin6->sin6_port, sizeof(unsigned short));
		idx += sizeof(unsigned short);

		*sz_bufP = idx;
	}
	return 0;
}


void
socks_initialize_proxy(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_initialize_proxy] sc => %p\n", sc);
	
	socks_realloc_buf(&sc->proxy_read_buf, &sc->proxy_read_size, MAX_RCVBUF);
	sc->proxy_read_idx = 0;
	sc->proxy_read_checked_idx = 0;

	switch (sc->service_cmd) {
	case SOCKS_CMD_CONNECT:
		socks_initialize_connect_proxy(sc);
		break;
	case SOCKS_CMD_BIND:
		socks_initialize_bind_proxy(sc);
		break;
	case SOCKS5_CMD_UDP_ASSOCIATE:
		/* socks_initialize_udp_proxy(sc); */
		break;
	}
}


void
socks_initialize_connect_proxy(struct socks_conn *sc)
{
	int result = 0;
	int error = 0;
#ifdef WIN32
	SOCKET s = INVALID_SOCKET;
#else
	int s = -1;
#endif
	struct addrinfo hints, *res, *res0;
	struct addrinfo *bind_res = NULL;
	struct sockaddr_storage addr;
	socklen_t addrlen;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

	int instance_id = -1;
	unsigned int assoc_id = 0;
	SCTP_InstanceParameters instanceParams;
	int i;
	int established = 0;

	unsigned char (*local_addrs)[SCTP_MAX_IP_LEN];
	int local_addrs_num;

	syslog(SYSLOG_VERBOSE, "[socks_initialize_connect_proxy] sc(%p)\n", sc);

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;

	if (sc->fwd != NULL) {
		/* Connecting another SOCKS... */

		error = getnameinfo(sc->fwd->ai_addr, sc->fwd->ai_addrlen,
		    hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
		    NI_NUMERICHOST | NI_NUMERICSERV);
		if (error < 0) {
			syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] "
			    "getnameinfo failed: %s",
			    gai_strerror(error));
			return;
		}
		syslog(SYSLOG_VERBOSE, "[socks_initialize_connect_proxy] fwd(%s/%s)\n",
		    hbuf, sbuf);

		sc->connect_res = sc->fwd;
	} else {
		/* Connecting directly... */

		memset(&hints, 0, sizeof(hints));
		hints.ai_family = PF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;

		error = getaddrinfo(sc->hbuf, sc->sbuf, &hints, &res0);
		if (error < 0) {
			syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] getaddrinfo failed%s",
			    gai_strerror(error));
			socks_close_service(sc);
			return;
		}

		syslog(SYSLOG_VERBOSE, "[socks_initialize_connect_proxy] connect(%s/%s)\n",
		    sc->hbuf, sc->sbuf);

		sc->connect_res = res0;
	}

	if (getnameinfo(sc->connect_res->ai_addr, sc->connect_res->ai_addrlen,
		hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
		NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
		socks_notify("[TARGET:CONNECT] id=%p, to_addr=%s, to_port=%s\n",
		    sc, hbuf, sbuf);
	}

	switch (sc->fd.type) {
	case SOCKS_FD_TYPE_TCP_SOCKET:
		sc->proxy_fd.type =
		    native_sctp ? SOCKS_FD_TYPE_SCTP_SOCKET : SOCKS_FD_TYPE_SCTP_SCTPLIB;
		break;
	case SOCKS_FD_TYPE_SCTP_SOCKET:
	case SOCKS_FD_TYPE_SCTP_SCTPLIB:
		sc->proxy_fd.type = SOCKS_FD_TYPE_TCP_SOCKET;
		break;
	default:
		break;
	}
	syslog(SYSLOG_VERBOSE, "[socks_initialize_connect_proxy] fd_type(%d)\n", sc->proxy_fd.type);

	if (sc->proxy_fd.type == SOCKS_FD_TYPE_TCP_SOCKET ||
	    sc->proxy_fd.type == SOCKS_FD_TYPE_SCTP_SOCKET) {
		/* Connecting via BSD Socket... */
		if (sc->bind_res != NULL) {
			res = sc->bind_res;
		} else {
			res = sc->connect_res;
		}
		while (res != NULL) {
			if (sc->proxy_fd.type == SOCKS_FD_TYPE_SCTP_SOCKET) {
				res->ai_protocol = IPPROTO_SCTP;
			}
			s = bsd_socket(&res);
#ifdef WIN32
			if (s == INVALID_SOCKET) {
#else
			if (s < 0) {
#endif
				return;
			}

			syslog(SYSLOG_VERBOSE, "socks_initialize_connect_proxy] fd(%d)\n",
			    s);
			if (sc->bind_res != NULL) {
				if (bsd_bind(s, res) < 0) {
#ifdef WIN32
					closesocket(s);
					s = INVALID_SOCKET;
#else
					close(s);
					s = -1;
#endif
					if (res != NULL) {
						res = res->ai_next;
					}
					continue;
				}
					
				error = bsd_connect(s, sc->connect_res);
			} else {
				error = bsd_connect(s, res);
			}

			if (error == 0) {
				established = 1;
#ifdef WIN32
			} else if (error != WSAEWOULDBLOCK) {
#else
			} else if (error != EINPROGRESS) {
#endif
				syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] error(%d)\n",
				    error);
#ifdef WIN32
				closesocket(s);
				s = INVALID_SOCKET;
#else
				close(s);
				s = -1;
#endif
				if (res != NULL) {
					res = res->ai_next;
				}
				continue;
			}

			addrlen = sizeof(addr);
			result = getsockname(s, (struct sockaddr *)&addr, &addrlen);
			if (result < 0) {
#ifdef WIN32
				error = WSAGetLastError();
#else
				error = errno;
#endif
			}
			if (result < 0) {
				syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] getsockname failed: %d\n",
				    error);
#ifdef WIN32
				closesocket(s);
				s = INVALID_SOCKET;
#else
				close(s);
				s = -1;
#endif
			}
			break;
		}

#ifdef WIN32
		if (s == INVALID_SOCKET) {
#else
		if (s == -1) {
#endif
			syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] no valid socket!\n");
			socks_notify("[TARGET:FAILED] id=%p\n",
			    sc);
			socks_close_service(sc);
			return;
		}

		getnameinfo((struct sockaddr *)&addr, addrlen,
		    hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);

		memset(&hints, 0, sizeof(hints));
		hints.ai_socktype = SOCK_STREAM;
		getaddrinfo(hbuf, sbuf, &hints, &sc->bind_res);

		sc->proxy_fd.bsd_fd = s;
		sc->proxy_fd.bsd_callback = &socks_proxy_bsd_handler;

		syslog(SYSLOG_VERBOSE, "[socks_initialize_connect_proxy] fd(%d)\n",
		    sc->proxy_fd.bsd_fd);

		if (!established) {
			/* Waiting for ESTABLISHED by POLLOUT */
			sc->proxy_fd.bsd_mask = POLLIN|POLLOUT;
			sc->proxy_fd.bsd_flags |= FD_FLAGS_CONNECTING;
		} else {
			socks_notify("[TARGET:CONNECTED] id=%p\n", sc);
			socks_proxy_start(sc);
			sc->proxy_fd.bsd_mask = POLLIN;
		}
		sctp_registerUserCallback((int)sc->proxy_fd.bsd_fd,
		    sc->proxy_fd.bsd_callback, sc, sc->proxy_fd.bsd_mask);
	} else {
		/* Connecting via sctplib.... */
		memset(&sc->proxy_fd.sctp_callbacks, 0,
		    sizeof(sc->proxy_fd.sctp_callbacks));

		sc->proxy_fd.sctp_callbacks.communicationUpNotif =
		    &socks_proxy_established_handler;
		sc->proxy_fd.sctp_callbacks.communicationLostNotif =
		    &socks_proxy_lost_handler;
		sc->proxy_fd.sctp_callbacks.peerShutdownReceivedNotif =
		    &socks_proxy_shutdown_handler;
		sc->proxy_fd.sctp_callbacks.shutdownCompleteNotif =
		    &socks_proxy_close_handler;
		sc->proxy_fd.sctp_callbacks.asconfStatusNotif =
		    &socks_proxy_asconf_handler;
		sc->proxy_fd.sctp_callbacks.dataArriveNotif =
		    &socks_proxy_read_handler;
		sc->proxy_fd.sctp_callbacks.queueStatusChangeNotif =
		    &socks_proxy_queue_status_handler;
		sc->proxy_fd.sctp_callbacks.networkStatusChangeNotif =
		    &socks_proxy_network_status_handler;

		memset(&hints, 0, sizeof(hints));
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_flags = AI_PASSIVE;

		error = getaddrinfo(NULL, "0", &hints, &res0);
		if (error < 0 || res0 == NULL) {
			syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] getaddrinfo failed: %s",
			    gai_strerror(error));
			socks_close_service(sc);
			return;
		}
		sc->bind_res = res0;

		instance_id = sctp_bind(sc->bind_res,
		    &sc->proxy_fd.sctp_callbacks, NULL);
		if (instance_id < 0)
		{
			socks_close_service(sc);
			return;
		}

		if ((result = sctp_getAssocDefaults(instance_id, &instanceParams)) != SCTP_SUCCESS) {
			sctp_unregisterInstance(instance_id);
			socks_close_service(sc);
			return;
		}

		instanceParams.assocMaxRetransmits = 1000;
		instanceParams.pathMaxRetransmits = 500;
		instanceParams.maxSendQueue = 1000;
		instanceParams.myRwnd = MAX_DATA_SIZE;
		if ((result = sctp_setAssocDefaults(instance_id, &instanceParams)) !=
		    SCTP_SUCCESS) {
			sctp_unregisterInstance(instance_id);
			socks_close_service(sc);
			return;
		}

		local_addrs = malloc(sizeof(*local_addrs) * instanceParams.noOfLocalAddresses);
		if (local_addrs == NULL) {
			syslog(SYSLOG_ERROR, "[socks_initialize_connect_proxy] malloc failed\n");
			sctp_unregisterInstance(instance_id);
			socks_close_service(sc);
			return;
		}
		memset(local_addrs, 0,
		    sizeof(*local_addrs) * instanceParams.noOfLocalAddresses);

		local_addrs_num = sctp_getLocalAddresses(instance_id,
		    local_addrs, instanceParams.noOfLocalAddresses);
		bind_res = NULL;
		for (i = 0; i < local_addrs_num; i++) {
			memset(&hints, 0, sizeof(hints));
			hints.ai_socktype = SOCK_STREAM;

			error = getaddrinfo((char *)local_addrs[i],
			    NULL, &hints, &res0);
			if (error < 0) {
				continue;
			}

			if (bind_res == NULL) {
				bind_res = res0;
			} else {
				for (res = bind_res;
				     res->ai_next; res = res->ai_next)
					;
				res->ai_next = res0;
			}
		}
		free(local_addrs);

		if (sc->bind_res != NULL) {
			freeaddrinfo(sc->bind_res);
		}
		sc->bind_res = bind_res;

		assoc_id = sctp_connect(instance_id, sc->connect_res, sc);
		if (assoc_id == 0)
		{
			sctp_unregisterInstance(instance_id);
			socks_close_service(sc);
			return;
		}

		sc->proxy_fd.sctp_instance_id = instance_id;
		sc->proxy_fd.sctp_assoc_id = assoc_id;
		sc->proxy_fd.sctp_sndmax = instanceParams.maxSendQueue;

		syslog(SYSLOG_VERBOSE, "[socks_initialize_connect_proxy] association(%d)\n",
		    assoc_id);
	}
}


void
socks_initialize_bind_proxy(struct socks_conn *sc)
{
}


void
socks_proxy_bsd_handler(int s, short int event, short int *pevent, void *data)
{
	struct socks_conn *sc = (struct socks_conn *)data;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_handler] fd(%d), event(%d), sc(%p)\n",
	    s, event, sc);
	if ((event & POLLIN) == POLLIN) {
		socks_proxy_bsd_read_handler(sc);
	}
	if ((event & POLLOUT) == POLLOUT) {
		if (sc->proxy_fd.bsd_flags & FD_FLAGS_CONNECTING) {
			/* Connection established */
			*pevent &= ~POLLOUT;
			sc->proxy_fd.bsd_flags &= ~FD_FLAGS_CONNECTING;
			if (sc->proxy_fd.bsd_fd == s) {
				socks_proxy_bsd_established_handler(sc);
			}
		} else {
			socks_proxy_bsd_send_handler(sc);
		}
	} 
	if ((event & POLLHUP) == POLLHUP) {
		/* Connection closed. */
		socks_proxy_bsd_close_handler(sc);
		return;
	}

	if (sc->service_read_checked_idx < sc->service_read_idx) {
		/* Outstanding data exists. */
		sc->proxy_fd.bsd_mask |= POLLOUT;
		*pevent = sc->proxy_fd.bsd_mask;
	} else {
		/* No outstanding data. */
		sc->proxy_fd.bsd_mask &= ~POLLOUT;
		*pevent = sc->proxy_fd.bsd_mask;
	}
}


void
socks_proxy_bsd_established_handler(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_established_handler] sc(%p), fd(%d)\n",
	    sc, sc->proxy_fd.bsd_fd);

	socks_notify("[TARGET:CONNECTED] id=%p\n", sc);

	socks_proxy_start(sc);
}


void*
socks_proxy_established_handler(unsigned int assocID, int status,
    unsigned int noOfDestinations, unsigned short noOfInStreams,
    unsigned short noOfOutStreams, int associationSupportsPRSCTP,
    void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_established_handler] sc(%p), association(%d)\n",
	    sc, assocID);
		
	socks_notify("[TARGET:CONNECTED] id=%p\n", sc);

	socks_proxy_start(sc);
	return sc;
}


void
socks_proxy_bsd_read_handler(struct socks_conn *sc)
{
	ssize_t sz, left;
	int error = 0;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_read_handler] sc(%p), fd(%d), idx(%d), checked_idx(%d), size(%d)\n",
	    sc, sc->proxy_fd.bsd_fd,
	    sc->proxy_read_idx, sc->proxy_read_checked_idx, sc->proxy_read_size);

	if ((sc->proxy_read_idx == sc->proxy_read_size) &&
	    (sc->proxy_read_checked_idx > 0)) {
		sc->proxy_read_idx = 0;
	}
	if ((sc->proxy_read_idx > sc->proxy_read_checked_idx) ||
	    (sc->proxy_read_idx == 0 && sc->proxy_read_checked_idx == 0)) {
		left = sc->proxy_read_size - sc->proxy_read_idx;
	} else {
		left = sc->proxy_read_checked_idx - sc->proxy_read_idx;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_read_handler] left=%d, size=%d, idx=%d, checked_idx=%d\n",
	    left, sc->proxy_read_size, sc->proxy_read_idx, sc->proxy_read_checked_idx);

	if (left <= 0) {
		syslog(SYSLOG_ERROR, "[socks_proxy_bsd_read_handler] "
		    "no buffer space for additional data.... Sleep a while\n");
		sc->proxy_fd.bsd_mask &= ~POLLIN;
		sc->proxy_fd.bsd_flags |= FD_FLAGS_DATA_RECEIVED;
		sctp_changeUserCallback(sc->proxy_fd.bsd_fd, NULL, NULL,
		    sc->fd.bsd_mask);
		socks_proxy_read(sc);
		return;
	}

	if (left > sc->proxy_read_window_size) {
		sz = sc->proxy_read_window_size;
	} else {
		sz = left;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_read_handler] sz=%d, left=%d\n",
	    sz, left);

	sz = recv(sc->proxy_fd.bsd_fd,
	    &sc->proxy_read_buf[sc->proxy_read_idx], sz, 0);
	if (sz < 0) {
#ifdef WIN32
		error = WSAGetLastError();
#else
		error = errno;
#endif
		syslog(SYSLOG_ERROR, "[socks_proxy_bsd_read_handler] recv failed: %d\n",
		    error);
	}
	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_read_handler] recv: %d\n", sz);

	if (sz > 0) {
		sc->proxy_read_idx += sz;
		socks_proxy_read(sc);
	} else if (
#ifdef WIN32
	    sz == 0 || error != WSAEWOULDBLOCK
#else
	    sz == 0 || error != EAGAIN
#endif
	    ) {
		socks_proxy_bsd_close_handler(sc);
	}
}


void
socks_proxy_read_handler(unsigned int assocID, unsigned short streamID,
    unsigned int len, unsigned short streamSN, unsigned int TSN,
    unsigned int protoID, unsigned int unordered, void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;
	size_t sz;
	int left;
	unsigned short ssn;
	unsigned int tsn;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_read_handler] sc(%p), association(%d), idx(%d), checked_idx(%d), size(%d)\n",
	    sc, assocID,
	    sc->proxy_read_idx, sc->proxy_read_checked_idx, sc->proxy_read_size);

	if ((sc->proxy_read_idx == sc->proxy_read_size) &&
	    (sc->proxy_read_checked_idx > 0)) {
		sc->proxy_read_idx = 0;
	}
	if ((sc->proxy_read_idx > sc->proxy_read_checked_idx) ||
	    (sc->proxy_read_idx == 0 && sc->proxy_read_checked_idx == 0)) {
		left = sc->proxy_read_size - sc->proxy_read_idx;
	} else {
		left = sc->proxy_read_checked_idx - sc->proxy_read_idx;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_read_handler] left=%d, size=%d, idx=%d, checked_idx=%d\n",
	    left, sc->proxy_read_size, sc->proxy_read_idx, sc->proxy_read_checked_idx);

	if (left <= 0) {
		syslog(SYSLOG_ERROR, "[socks_proxy_read_handler] "
		    "no buffer space for additional data.... Sleep a while\n");
		socks_proxy_read(sc);
		return;
	}

	if (left > sc->proxy_read_window_size) {
		sz = sc->proxy_read_window_size;
	} else {
		sz = left;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_read_handler] sz=%d, left=%d\n",
	    sz, left);

	if (sctp_receive(assocID, streamID,
	    (unsigned char *)&sc->proxy_read_buf[sc->proxy_read_idx],
	    (unsigned int *)&sz, &ssn, &tsn, SCTP_MSG_DEFAULT) != SCTP_SUCCESS) {
		return;
	}
	syslog(SYSLOG_VERBOSE, "[socks_proxy_read_handler] sctp_receive: %d\n", sz);
	sc->proxy_read_idx += sz;

	socks_proxy_read(sc);
}


void
socks_proxy_read(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_proxy_read] sc(%p), state(%d)\n",
	    sc, sc->service_state);
	if (sc->service_state != SERVICE_STATE_DO_SERVICE) {
		syslog(SYSLOG_VERBOSE, "[socks_proxy_read] !SERVICE_STATE_DO_SERVICE\n");
		return;
	}
	socks_service_send(sc, NULL, 0);
}


void
socks_proxy_suspend_read(struct socks_conn *sc)
{
	int result;
	SCTP_AssociationStatus assocStatus;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_suspend_read] sc=%p, state=%d\n",
	    sc, sc->proxy_state);

	if (sc->proxy_state >= PROXY_STATE_WAIT_CLOSE) {
		return;
	}

	switch (sc->proxy_fd.type) {
	case SOCKS_FD_TYPE_TCP_SOCKET:
	case SOCKS_FD_TYPE_SCTP_SOCKET:
		sc->proxy_fd.bsd_mask &= ~POLLIN;
		sctp_changeUserCallback(sc->proxy_fd.bsd_fd, NULL, NULL,
		    sc->proxy_fd.bsd_mask);
		break;
	case SOCKS_FD_TYPE_SCTP_SCTPLIB:
		if ((result = sctp_getAssocStatus(sc->proxy_fd.sctp_assoc_id, &assocStatus)) == SCTP_SUCCESS) {
			assocStatus.myRwnd = 0;
			result = sctp_setAssocStatus(sc->proxy_fd.sctp_assoc_id, &assocStatus);
			if (result == SCTP_SUCCESS) {
				syslog(SYSLOG_VERBOSE, "[socks_proxy_suspend_read] sc=%p, myRwnd=%d\n",
				    sc, assocStatus.myRwnd);
			} else {
				syslog(SYSLOG_ERROR, "[socks_proxy_suspend_read] sctp_setAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
				    result, sc, assocStatus.myRwnd);
			}
		} else {
			syslog(SYSLOG_ERROR, "[socks_proxy_suspend_read] sctp_setAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
			    result, sc, 0);
		}
		break;
	default:
		break;
	}
}


void
socks_proxy_resume_read(struct socks_conn *sc)
{
	int result;
	SCTP_AssociationStatus assocStatus;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_resume_read] sc(%p), state=%d\n",
	    sc, sc->proxy_state);

	if (sc->proxy_state >= PROXY_STATE_WAIT_CLOSE) {
		return;
	}

	switch (sc->proxy_fd.type) {
	case SOCKS_FD_TYPE_TCP_SOCKET:
	case SOCKS_FD_TYPE_SCTP_SOCKET:
		sc->proxy_fd.bsd_mask |= POLLIN;
		sctp_changeUserCallback(sc->proxy_fd.bsd_fd, NULL, NULL,
		    sc->proxy_fd.bsd_mask);
		if (1 || (sc->proxy_fd.bsd_flags & FD_FLAGS_DATA_RECEIVED)) {
			/* Data arraived... */
			socks_proxy_bsd_read_handler(sc);
		}
		break;
	case SOCKS_FD_TYPE_SCTP_SCTPLIB:
		if ((result = sctp_getAssocStatus(sc->proxy_fd.sctp_assoc_id, &assocStatus)) == SCTP_SUCCESS) {
			if (assocStatus.myRwnd == 0) {
				assocStatus.myRwnd /= 2;
			}
			assocStatus.myRwnd = sc->proxy_read_window_size;
			result = sctp_setAssocStatus(sc->proxy_fd.sctp_assoc_id, &assocStatus);
			if (result == SCTP_SUCCESS) {
				syslog(SYSLOG_VERBOSE, "[socks_proxy_resume_read] sc=%p, myRwnd=%d\n",
				    sc, assocStatus.myRwnd);
			} else {
				syslog(SYSLOG_ERROR, "[socks_proxy_resume_read] sctp_setAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
				    result, sc, assocStatus.myRwnd);
			}
		} else {
			syslog(SYSLOG_ERROR, "[socks_proxy_resume_read] sctp_getAssocStatus failed, result=%d, sc=%p, myRwnd=%d\n",
			    result, sc, sc->proxy_read_window_size);
		}
		break;
	default:
		break;
	}
}


int
socks_proxy_bsd_send_handler(struct socks_conn *sc)
{
	int error = 0;
	int sz, send_sz, real_send_sz;
	int sent_sz = 0, second_sent_sz = 0;

	if (sc->proxy_state != PROXY_STATE_DO_SERVICE) {
		return -1;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] sc(%p), association(%d), size(%d), idx(%d), sent_idx(%d)\n",
	    sc, sc->proxy_fd.bsd_fd,
	    sc->service_read_size, sc->service_read_idx, sc->service_read_checked_idx);
	
	if ((sc->service_read_idx == 0) && (sc->service_read_checked_idx == 0)) {
		syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] Already all data transmitted....\n");
		return 0;
	}

	if (sc->service_read_idx > sc->service_read_checked_idx) {
		send_sz = sc->service_read_idx - sc->service_read_checked_idx;
	} else {
		send_sz = sc->service_read_size - sc->service_read_checked_idx;
		if (send_sz == 0) {
			sc->service_read_checked_idx = 0;
			send_sz = sc->service_read_idx - sc->service_read_checked_idx;
		}
	}

	if (send_sz > MAX_DATA_SIZE) {
		real_send_sz = MAX_DATA_SIZE;
	} else {
		real_send_sz = send_sz;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] real_send_sz=%d, send_sz=%d, idx=%d, sent_idx=%d\n",
	    real_send_sz, send_sz,
	    sc->service_read_idx, sc->service_read_checked_idx);

	sz = send(sc->proxy_fd.bsd_fd,
	    &sc->service_read_buf[sc->service_read_checked_idx],
	    real_send_sz, 0);
	if (sz < 0) {
#ifdef WIN32
		error = WSAGetLastError();
#else
		error = errno;
#endif
	}

	if (sz > 0) {
		sent_sz += sz;
		sc->service_read_checked_idx += sz;
		sc->service_read_window_size += sz / 2;
	}

	if (sz == real_send_sz) {
		if ((real_send_sz == send_sz) &&
		    (sc->service_read_checked_idx == sc->service_read_idx)) {
			syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] No remaining data....\n");
			sc->service_read_idx = 0;
			sc->service_read_checked_idx = 0;
			socks_service_resume_read(sc);
		} else if ((real_send_sz == send_sz) &&
		    (sc->service_read_checked_idx == sc->service_read_size)) {
			syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] Remaining data exists before....\n");
			sc->service_read_checked_idx = 0;
			second_sent_sz = socks_proxy_bsd_send_handler(sc);
			if (second_sent_sz > 0) {
				sent_sz += second_sent_sz;
			}
		} else {
			syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] Remaining data exists....\n");
			second_sent_sz = socks_proxy_bsd_send_handler(sc);
			if (second_sent_sz > 0) {
				sent_sz += second_sent_sz;
			}
		}
	} else if ((sz < 0 &&
#ifdef WIN32
	    (error == WSAENOBUFS || error == WSAEWOULDBLOCK)
#else
	    (error == EWOULDBLOCK || error == EMSGSIZE)
#endif
	    ) || (sz < real_send_sz)) {
		syslog(SYSLOG_ERROR, "[socks_proxy_bsd_send_handler]  sz=%d, Remaining data exists #1n", sz);

		sc->proxy_fd.bsd_mask |= POLLOUT;
		sctp_changeUserCallback(sc->proxy_fd.bsd_fd, NULL, NULL,
		    sc->proxy_fd.bsd_mask);
		socks_service_suspend_read(sc);
		return sz < 0 ? 0 : sz;
	} else if (sz < 0) {
		syslog(SYSLOG_ERROR, "[socks_proxy_bsd_send_handler] send failed: %d\n",
		    error);
		socks_close_service(sc);
		socks_close_proxy(sc);
		return -1;
	}
	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_send_handler] send: %d\n", sz);

	if (sc->service_state >= SERVICE_STATE_FIN && sc->service_read_idx == 0) {
		socks_close_proxy(sc);
	}

	return sz;
}


int
socks_proxy_send_handler(struct socks_conn *sc)
{
	size_t send_sz = 0, real_send_sz = 0;
	int sent_sz = 0, second_sent_sz = 0;
	int result = 0;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_send_handler] association(%d), size(%d), idx(%d), sent_idx(%d)\n",
	    sc->proxy_fd.sctp_assoc_id,
	    sc->service_read_size, sc->service_read_idx, sc->service_read_checked_idx);

	if ((sc->service_read_idx == 0) && (sc->service_read_checked_idx == 0)) {
		syslog(SYSLOG_VERBOSE, "[socks_proxy_send_handler] Already all data transmitted....\n");
		return 0;
	}

	if (sc->service_read_idx > sc->service_read_checked_idx) {
		send_sz = sc->service_read_idx - sc->service_read_checked_idx;
	} else {
		send_sz = sc->service_read_size - sc->service_read_checked_idx;
		if (send_sz == 0) {
			sc->service_read_checked_idx = 0;
			send_sz = sc->service_read_idx - sc->service_read_checked_idx;
		}
	}

	if (send_sz > SCTP_MAXIMUM_DATA_LENGTH) {
		real_send_sz = SCTP_MAXIMUM_DATA_LENGTH;
	} else {
		real_send_sz = send_sz;
	}

	syslog(SYSLOG_VERBOSE, "[socks_proxy_send_handler] real_send_sz=%d, send_sz=%d, idx=%d, sent_idx=%d\n",
	    real_send_sz, send_sz, sc->service_read_idx, sc->service_read_checked_idx);

	result = sctp_send(sc->proxy_fd.sctp_assoc_id, 0, 
	    (unsigned char *)&sc->service_read_buf[sc->service_read_checked_idx],
	    real_send_sz, 0, SCTP_USE_PRIMARY, SCTP_NO_CONTEXT,
	    SCTP_INFINITE_LIFETIME, 0, SCTP_BUNDLING_DISABLED);
	if (result == SCTP_SUCCESS) {
		sent_sz += real_send_sz;
		sc->service_read_checked_idx += real_send_sz;
		sc->service_read_window_size += real_send_sz / 2;

		if ((real_send_sz == send_sz) &&
		    (sc->service_read_checked_idx == sc->service_read_idx)) {
			syslog(SYSLOG_VERBOSE, "[socks_proxy_send_handler] No remaining data....\n");
			sc->service_read_idx = 0;
			sc->service_read_checked_idx = 0;
			socks_service_resume_read(sc);
		} else if ((real_send_sz == send_sz) &&
		    (sc->service_read_checked_idx == sc->service_read_size)) {
			syslog(SYSLOG_VERBOSE, "[socks_proxy_send_handler] Remaining data exists before....\n");
			sc->service_read_checked_idx = 0;
			second_sent_sz = socks_proxy_send_handler(sc);
			if (second_sent_sz > 0) {
				sent_sz += second_sent_sz;
			}
		} else {
			syslog(SYSLOG_VERBOSE, "[socks_proxy_send_handler] Remaining data exists....\n");
			second_sent_sz = socks_proxy_send_handler(sc);
			if (second_sent_sz > 0) {
				sent_sz += second_sent_sz;
			}
		}
	} else if (result == SCTP_QUEUE_EXCEEDED) {
		syslog(SYSLOG_ERROR, "[socks_proxy_send_handler] sctp_send return SCTP_QUEUE_EXCEEDED\n");
		sc->proxy_fd.sctp_flags |= FD_FLAGS_EXCEED_SEND;
		socks_service_suspend_read(sc);
		return 0;
	} else {
		syslog(SYSLOG_ERROR, "[socks_proxy_send_handler] sctp_send failed");
		socks_close_service(sc);
		socks_close_proxy(sc);
		return -1;
	}

	if (sc->service_state >= SERVICE_STATE_FIN && sc->service_read_idx == 0) {
		socks_close_proxy(sc);
	}

	return sent_sz;
}


int
socks_proxy_send(struct socks_conn *sc, char *str, size_t sz)
{
	int result = 0;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_send] sc(%p), str(%p), size(%d)\n",
	    sc, str, sz);

	if (sc->proxy_fd.type == SOCKS_FD_TYPE_TCP_SOCKET ||
	    sc->proxy_fd.type == SOCKS_FD_TYPE_SCTP_SOCKET) {
		syslog(SYSLOG_VERBOSE, "[socks_proxy_send]#1\n");
		result = socks_proxy_bsd_send_handler(sc);
	}  else {
		syslog(SYSLOG_VERBOSE, "[socks_proxy_send]#2\n");
		result = socks_proxy_send_handler(sc);
	}

	return result;
}


void
socks_proxy_queue_status_handler(unsigned int assocID,
    int queueType, int queueId, int queueLen, void *ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_queue_status_handler] sc(%p), association(%d), queueType(%d), queueId(%d), queueLen(%d)\n",
	    sc, assocID, queueType, queueId, queueLen);

	if (queueType != SCTP_SEND_QUEUE) {
		return;
	}

	if (sc->proxy_fd.sctp_flags & FD_FLAGS_EXCEED_SEND) {
		sc->proxy_fd.sctp_flags &= ~FD_FLAGS_EXCEED_SEND;
		socks_proxy_send_handler(sc);
	}
}


void
socks_proxy_bsd_close_handler(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_proxy_bsd_close_handler] sc(%d), fd(%d)\n",
	    sc, sc->proxy_fd.bsd_fd);

	socks_notify("[TARGET:CLOSED] id=%p\n", sc);

	sctp_unregisterUserCallback((int)sc->proxy_fd.bsd_fd);
#ifdef WIN32
	closesocket(sc->proxy_fd.bsd_fd);
	sc->proxy_fd.bsd_fd = INVALID_SOCKET;
#else
	close(sc->proxy_fd.bsd_fd);
	sc->proxy_fd.bsd_fd = -1;
#endif

	switch (sc->service_state) {
	case SERVICE_STATE_DO_SERVICE:
		sc->proxy_state = PROXY_STATE_WAIT_CLOSE;
		if (sc->proxy_read_idx == 0) {
			/* No remaining data. Close service side. */
			socks_close_service(sc);
		}
		break;
	case SERVICE_STATE_FIN:
		sc->proxy_state = PROXY_STATE_CLOSE;
		socks_free_proxy(sc);
		break;
	case SERVICE_STATE_WAIT_CLOSE:
		sc->proxy_state = PROXY_STATE_CLOSE;
		socks_free_proxy(sc);
		socks_free_service(sc);
		break;
	default:
		break;
	}
}


void socks_proxy_lost_handler(unsigned int assocID, unsigned short eventType,
    void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_lost_handler] sc(%p), association(%d) type(%d)\n",
	    sc, assocID, eventType);

	socks_notify("[TARGET:ABORTED] id=%p\n", sc);

	sctp_deleteAssociation(assocID);
	sc->proxy_fd.sctp_assoc_id = -1;
	if (sc->proxy_fd.sctp_instance_id != -1) {
		sctp_unregisterInstance(sc->proxy_fd.sctp_instance_id);
	}

	switch (sc->service_state) {
	case SERVICE_STATE_DO_SERVICE:
		sc->proxy_state = PROXY_STATE_WAIT_CLOSE;
		if (sc->proxy_read_idx == 0) {
			/* No remaining data. Close service side. */
			socks_close_service(sc);
		}
		break;
	case SERVICE_STATE_FIN:
		sc->proxy_state = PROXY_STATE_CLOSE;
		socks_free_proxy(sc);
		break;
	case SERVICE_STATE_WAIT_CLOSE:
		sc->proxy_state = PROXY_STATE_CLOSE;
		socks_free_proxy(sc);
		socks_free_service(sc);
		break;
	default:
		break;
	}
}


void socks_proxy_shutdown_handler(unsigned int assocID, void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_shutdown_handler] sc(%p), association(%d)\n",
	    sc, assocID);

	socks_notify("[TARGET:CLOSING] id=%p\n", sc);

	sc->proxy_state = PROXY_STATE_FIN;
	switch (sc->service_state) {
	case SERVICE_STATE_DO_SERVICE:
		if (sc->proxy_read_idx == 0) {
			/* No remaining data. Close service side. */
			socks_close_service(sc);
		}
		break;
	default:
		break;
	}
}


void socks_proxy_close_handler(unsigned int assocID, void* ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_close_handler] sc(%p), association(%d)\n",
	    sc, assocID);

	socks_notify("[TARGET:CLOSED] id=%p\n", sc);

	sctp_deleteAssociation(assocID);
	sc->proxy_fd.sctp_assoc_id = -1;
	sctp_unregisterInstance(sc->proxy_fd.sctp_instance_id);

	switch (sc->service_state) {
	case SERVICE_STATE_DO_SERVICE:
		sc->proxy_state = PROXY_STATE_WAIT_CLOSE;
		if (sc->proxy_read_idx == 0) {
			/* No remaining data. Close service side. */
			socks_close_service(sc);
		}
		break;
	case SERVICE_STATE_FIN:
		sc->proxy_state = PROXY_STATE_CLOSE;
		socks_free_proxy(sc);
		break;
	case SERVICE_STATE_WAIT_CLOSE:
		sc->proxy_state = PROXY_STATE_CLOSE;
		socks_free_proxy(sc);
		socks_free_service(sc);
		break;
	default:
		break;
	}
}


void socks_proxy_asconf_handler(unsigned int assocID, unsigned short type,
    unsigned char address[SCTP_MAX_IP_LEN], int result, void *ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_asconf_handler] sc(%p), association(%d) "
	    "type(%d), %s, result(%d)\n", sc, assocID, address, result);

	switch (type) {
	case SCTP_ASCONF_ADD:
		socks_notify("[SCTP:LOCAL] ADD %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_ADD_SUCCESS:
		socks_notify("[SCTP:LOCAL] ADD SUCCESS %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_ADD_FAIL:
		socks_notify("[SCTP:LOCAL] ADD FAILED %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_DEL:
		socks_notify("[SCTP:LOCAL] DEL %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_DEL_SUCCESS:
		socks_notify("[SCTP:LOCAL] DEL SUCCESS %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_DEL_FAIL:
		socks_notify("[SCTP:LOCAL] DEL FAILED %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_SET_PRIMARY:
		socks_notify("[SCTP:LOCAL] SET PRIMARY %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_SET_PRIMARY_SUCCESS:
		socks_notify("[SCTP:LOCAL] SET PRIMARY SUCCESS %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	case SCTP_ASCONF_SET_PRIMARY_FAIL:
		socks_notify("[SCTP:LOCAL] SET PRIMARY FAILED %s, result=%d, assoc=%u\n",
		    address, result, assocID);
		break;
	default:
		break;
	}
}


void
socks_proxy_network_status_handler(unsigned int assocID, short destKey,
    unsigned char destStr[SCTP_MAX_IP_LEN], unsigned short status, void *ulpDataPtr)
{
	struct socks_conn *sc = (struct socks_conn *)ulpDataPtr;

	syslog(SYSLOG_VERBOSE, "[socks_proxy_network_status_handler] sc=%p, assoc=%u dest=%d, destStr=%s, status=%u\n",
	    sc, assocID, destKey, destStr, status);

	switch (status) {
	case SCTP_PATH_ADDED:
		socks_notify("[SCTP:PEER] ADD %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_REMOVED:
		socks_notify("[SCTP:PEER] DEL %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_ACTIVE:
		socks_notify("[SCTP:PEER] ACTIVE %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_INACTIVE:
		socks_notify("[SCTP:PEER] INACTIVE %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_CONFIRMED:
		socks_notify("[SCTP:PEER] CONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_UNCONFIRMED:
		socks_notify("[SCTP:PEER] UNCONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_SRC_CONFIRMED:
		socks_notify("[SCTP:PEER] SRC_CONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_SRC_UNCONFIRMED:
		socks_notify("[SCTP:PEER] SRC_UNCONFIRMED %s, assoc=%u\n", destStr, assocID);
		break;
	case SCTP_PATH_PRIMARY:
		socks_notify("[SCTP:PEER] PRIMARY %s, assoc=%u\n", destStr, assocID);
		break;
	default:
		break;
	}
}


void
socks_proxy_start(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_proxy_start] sc(%p)\n", sc);
	if (sc->fwd == NULL) {
		syslog(SYSLOG_VERBOSE, "[socks_proxy_start] sc(%p), service_protocol(%d)\n",
		    sc, sc->service_protocol);
		switch (sc->service_protocol) {
		case SOCKS_VERSION_4:
			socks4_response_cmd(sc, 90);
			break;
		case SOCKS_VERSION_5:
			socks5_response_cmd(sc, 0x00);
			break;
		default:
			break;
		}
	}
	socks_proxy_send(sc, NULL, 0);
	if (sc->service_state < SERVICE_STATE_DO_SERVICE) {
		sc->service_state = SERVICE_STATE_DO_SERVICE;
		sc->service_read_idx = 0;
		sc->service_read_checked_idx = 0;
	}
	sc->proxy_state = PROXY_STATE_DO_SERVICE;

	sc->service_read_window_size = MAX_DATA_SIZE;
	sc->proxy_read_window_size = MAX_DATA_SIZE;
}


void
socks_close_proxy(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_close_proxy] sc(%p), sc->service_state(%d)\n",
	    sc, sc != NULL ? sc->service_state : -1);

	if (sc->service_state < SERVICE_STATE_PREPARE_SERVICE) {
		return;
	}

	if (sc->proxy_fd.type == SOCKS_FD_TYPE_TCP_SOCKET ||
	    sc->proxy_fd.type == SOCKS_FD_TYPE_SCTP_SOCKET) {
		syslog(SYSLOG_VERBOSE, "[socks_close_proxy] sc(%p), fd(%d)\n",
		    sc, sc->proxy_fd.bsd_fd);

#ifdef WIN32
		if (sc->proxy_fd.bsd_fd != INVALID_SOCKET) {
#else
		if (sc->proxy_fd.bsd_fd != -1) {
#endif
			socks_notify("[TARGET:CLOSED] id=%p\n", sc);
			sctp_unregisterUserCallback((int)sc->proxy_fd.bsd_fd);
#ifdef WIN32
			closesocket(sc->proxy_fd.bsd_fd);
			sc->proxy_fd.bsd_fd = INVALID_SOCKET;
#else
			close(sc->proxy_fd.bsd_fd);
			sc->proxy_fd.bsd_fd = -1;
#endif
			socks_free_proxy(sc);
		}
	} else {
		syslog(SYSLOG_VERBOSE, "[socks_close_proxy] sc(%p), asscoiation(%d)\n",
		    sc, sc->proxy_fd.sctp_assoc_id);
		if (sc->proxy_fd.sctp_assoc_id != -1) {
			sctp_shutdown(sc->proxy_fd.sctp_assoc_id);
		}
	}
}


void
socks_free_proxy(struct socks_conn *sc)
{
	syslog(SYSLOG_VERBOSE, "[socks_free_proxy] sc(%p), proxy_read_buf(%p)\n",
	    sc, sc->proxy_read_buf);

	if (sc->proxy_read_buf != NULL) {
		free(sc->proxy_read_buf);
		sc->proxy_read_buf = NULL;
	}
}


void
socks_realloc_buf(char **bufP, size_t *max_sizeP, size_t request_size)
{
	if (*max_sizeP == 0)
	{
		*max_sizeP = 1024 * ((request_size / 1024) + 1);
		*bufP = malloc(*max_sizeP);
	} else if (*max_sizeP < request_size)
	{
		*max_sizeP = 1024 * ((request_size / 1024) + 1);
		*bufP = realloc(*bufP, *max_sizeP);
	} else
	{
		return;
	}

	if (*bufP == NULL)
	{
		syslog(SYSLOG_ERROR, "[socks_realloc_buf] Out of memory!!\n");
		exit(1);
	}
}


#ifdef WIN32
SOCKET
#else
int
#endif
bsd_socket(struct addrinfo **resP)
{
	int error = 0;
	struct addrinfo *res0, *res;
#ifdef WIN32
	SOCKET s = INVALID_SOCKET;
	DWORD rcv_buf_size = 65536;
	DWORD snd_buf_size = 65536;
#else
	int s = -1;
	int rcv_buf_size = 65536;
	int snd_buf_size = 65536;
#endif

	int reuse_addr = 0;
	unsigned long nonblock_flag = 0;

#ifdef IPV6_V6ONLY
	int v6only = 0;
#endif

	res0 = *resP;
	for (res = res0; res ; res = res->ai_next) {
		syslog(SYSLOG_VERBOSE, "[bsd_socket] res(%p)->ai_family(%d),"
		    "ai_socktype(%d), protocol(%d)\n",
		    res, res->ai_family, res->ai_socktype,
		    res->ai_protocol);

		s = socket(res->ai_family, res->ai_socktype,
		    res->ai_protocol);
		if (s < 0) {
#ifdef WIN32
			error = WSAGetLastError();
#else
			error = errno;
#endif
		}
		if (s < 0) {
			if (res->ai_protocol != IPPROTO_SCTP) {
				syslog(SYSLOG_ERROR, "[bsd_socket] socket failed: %d\n",
				    error);
			}
			continue;
		}

#ifdef IPV6_V6ONLY
		if (res->ai_family == AF_INET6 &&
		    res->ai_protocol != IPPROTO_SCTP) {
			v6only = 1;
			if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
				&v6only, sizeof(v6only)) < 0) {
				syslog(SYSLOG_ERROR, "[bsd_socket] setsockopt(IPV6_V6ONLY) failed: %d\n",
				    errno);
#ifdef WIN32
				closesocket(s);
				s = INVALID_SOCKET;
#else
				close(s);
				s = -1;
#endif
			}
		}
#endif

#ifdef WIN32
		nonblock_flag = 1;
		if (ioctlsocket(s, FIONBIO, &nonblock_flag) == SOCKET_ERROR) {
			error = WSAGetLastError();
			syslog(SYSLOG_ERROR, "[bsd_socket] ioctlsocket failed: %d\n", error);
			closesocket(s);
			s = INVALID_SOCKET;
			continue;
		}
#else
		nonblock_flag = fcntl(s, F_GETFL, 0);
		if (nonblock_flag < 0) {
			syslog(SYSLOG_ERROR, "[bsd_socket] "
			    "fcntl F_GETFL failed: %d\n", errno);
			close(s);
			s = -1;
			continue;
		}

		if (fcntl(s, F_SETFL, nonblock_flag | O_NONBLOCK) < 0) {
			syslog(SYSLOG_ERROR, "[bsd_socket] "
			    "fcntl F_SETFL failed: %d\n", errno);
#ifdef WIN32
			closesocket(s);
			s = INVALID_SOCKET;
#else
			close(s);
			s = -1;
#endif
			continue;
		}
#endif

		reuse_addr = 1;
		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
			(char *)&reuse_addr, sizeof(reuse_addr)) < 0) {
#ifdef WIN32
			error = WSAGetLastError();
#else
			error = errno;
#endif
			syslog(SYSLOG_ERROR, "[bsd_socket] setsockopt(SO_REUSEADDR) failed: %d\n",
			    error);

#ifdef WIN32
			closesocket(s);
			s = INVALID_SOCKET;
#else
			close(s);
			s = -1;
#endif
			continue;
		}

		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&rcv_buf_size,
			sizeof(rcv_buf_size)) < 0) {
#ifdef WIN32
			error = WSAGetLastError();
#else
			error = errno;
#endif
			syslog(SYSLOG_ERROR, "[bsd_socket] setsockopt(SO_RCVBUF): %d\n", error);

#ifdef WIN32
			closesocket(s);
			s = INVALID_SOCKET;
#else
			close(s);
			s = -1;
#endif
			continue;
		}

		if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&snd_buf_size,
			sizeof(snd_buf_size)) < 0) {
#ifdef WIN32
			error = WSAGetLastError();
#else
			error = errno;
#endif
			syslog(SYSLOG_ERROR, "[bsd_socket] setsockopt(SO_SNDBUF): %d\n", error);

#ifdef WIN32
			closesocket(s);
			s = INVALID_SOCKET;
#else
			close(s);
			s = -1;
#endif
			continue;
		}
		break;
	}

	if (res != NULL) {
		*resP = res;
	} else {
		*resP = NULL;
	}
	return s;
}


int
#ifdef WIN32
bsd_bind(SOCKET s, struct addrinfo *res)
#else
bsd_bind(int s, struct addrinfo *res)
#endif
{
	int error;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

	memset(hbuf, 0, sizeof(hbuf));
	memset(sbuf, 0, sizeof(sbuf));

	if (getnameinfo(res->ai_addr, res->ai_addrlen,
		hbuf, sizeof(hbuf),
		sbuf, sizeof(sbuf),
		NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
		syslog(SYSLOG_ERROR, "[bsd_bind] invalid addr in addrinfo(%p)\n",
		    res);
		return -1;
	}

	syslog(SYSLOG_VERBOSE, "[bsd_bind] binding %s/%s\n", hbuf, sbuf);
	if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
#ifdef WIN32
		error = WSAGetLastError();
#else
		error = errno;
#endif
		syslog(SYSLOG_ERROR, "[bsd_bind] bind(%s:%s) failed: %d\n",
		    hbuf, sbuf, error);
		return -1;
	}

	return 0;
}


int 
#ifdef WIN32
bsd_connect(SOCKET s, struct addrinfo *res)
#else
bsd_connect(int s, struct addrinfo *res)
#endif
{
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
#ifdef WIN32
	int result= SOCKET_ERROR;
#else
	int result= -1;
#endif

	memset(hbuf, 0, sizeof(hbuf));
	memset(sbuf, 0, sizeof(sbuf));

	if (getnameinfo(res->ai_addr, res->ai_addrlen,
		hbuf, sizeof(hbuf),
		sbuf, sizeof(sbuf),
		NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
		syslog(SYSLOG_ERROR, "[bsd_connect] invalid addr in addrinfo(%p)\n",
		    res);
		return -1;
	}

	syslog(SYSLOG_VERBOSE, "[bsd_connect] connecting %s/%s\n", hbuf, sbuf);

	result = connect(s, res->ai_addr, res->ai_addrlen);
#ifdef WIN32
	if (result == SOCKET_ERROR) {
		return WSAGetLastError();
#else
	if (result < 0) {
#endif
		return errno;
	} else {
		return 0;
	}
}


int
sctp_bind(struct addrinfo *res0, SCTP_ulpCallbacks *ulpCallbacksP, void *ptr)
{
	struct addrinfo *res;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
	unsigned int i;
	unsigned char (*client_addrs)[SCTP_MAX_IP_LEN];
	int client_addrs_num;
	int instance_id = -1;
	int result = 0;

	syslog(SYSLOG_VERBOSE, "[sctp_bind] res0(%p) ulpCallbacksP(%p)\n",
	    res0, ulpCallbacksP);

	client_addrs_num = 0;
	for (res = res0; res; res = res->ai_next)
	{
		client_addrs_num++;
	}
	if (client_addrs_num == 0)
	{
		client_addrs_num++;
	}

	client_addrs = malloc(sizeof(*client_addrs) * client_addrs_num);
	memset(client_addrs, 0, sizeof(*client_addrs) * client_addrs_num);

	i = 0;
	for (res = res0; res; res = res->ai_next)
	{
		memset(hbuf, 0, sizeof(hbuf));
		memset(sbuf, 0, sizeof(sbuf));
		if (getnameinfo(res->ai_addr, res->ai_addrlen,
		    hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
		    NI_NUMERICHOST | NI_NUMERICSERV))
		{
			syslog(SYSLOG_ERROR, "[sctp_bind] getnameinfo failed\n");
			continue;
		}
		syslog(SYSLOG_VERBOSE, "[sctp_bind] binding %s/%s\n", hbuf, sbuf);
		strncpy((char *)client_addrs[i], hbuf, SCTP_MAX_IP_LEN);
		i++;
	}
	if (i == 0)
	{
		return -1;
	}

	instance_id = sctp_registerInstance(atoi(sbuf),
	    MAXIMUM_NUMBER_OF_IN_STREAMS, MAXIMUM_NUMBER_OF_OUT_STREAMS,
	    i, client_addrs, *ulpCallbacksP, ptr);
	syslog(SYSLOG_VERBOSE, "[sctp_bind] instance_id(%d)\n", instance_id);

	if (instance_id < 0) {
		syslog(SYSLOG_ERROR, "[sctp_bind] sctp_getAssocDefaults failed: %d\n",
		    result);
	}
			    
	free(client_addrs);

	return instance_id;
}


unsigned int
sctp_connect(int instance_id, struct addrinfo *res0, void *ptr)
{
	struct addrinfo *res;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
	int num, i;
	unsigned char (*server_addrs)[SCTP_MAX_IP_LEN];
	int server_addrs_num;
	unsigned int assoc_id = 0;

	server_addrs_num = 0;
	for (res = res0; res; res = res->ai_next)
	{
		server_addrs_num++;
	}
	if (server_addrs_num == 0)
	{
		syslog(SYSLOG_ERROR, "[sctp_connect] No destination!!\n");
		return 0;
	}

	server_addrs = malloc(sizeof(*server_addrs) * server_addrs_num);
	memset(server_addrs, 0, sizeof(*server_addrs) * server_addrs_num);

	i = 0;
	for (res = res0; res; res = res->ai_next)
	{
		memset(hbuf, 0, sizeof(hbuf));
		memset(sbuf, 0, sizeof(sbuf));
		if (getnameinfo(res->ai_addr, res->ai_addrlen,
		    hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
		    NI_NUMERICHOST | NI_NUMERICSERV))
		{
			syslog(SYSLOG_ERROR, "[sctp_connect] getnameinfo failed\n");
			continue;
		}
		strncpy((char *)server_addrs[i], hbuf, SCTP_MAX_IP_LEN);
		i++;
	}
	num = i;
	for (i = 0; i < num; i++) {
		syslog(SYSLOG_VERBOSE, "[sctp_connect] sctp_associatex: %s/%s\n",
		    server_addrs[i], sbuf);
	}

	assoc_id = sctp_associatex(instance_id,
	    MAXIMUM_NUMBER_OF_OUT_STREAMS, server_addrs, num, 1,
	    atoi(sbuf), ptr);

	free(server_addrs);

	return assoc_id;
}


void
socks_notify(const char *str, ...)
{
	va_list ap;

	va_start(ap, str);
#ifdef HAVE_GUI
#ifdef WIN32
	WindowNotify(str, ap);
#endif
#else
	{
	struct timeval tv;
	struct tm *the_time;

	adl_gettime(&tv);
	the_time = localtime((time_t *) & (tv.tv_sec));

	fprintf(stderr, "%02d:%02d:%02d.%03d - ",
	    the_time->tm_hour,
	    the_time->tm_min, the_time->tm_sec, (int) (tv.tv_usec / 1000));
	vfprintf(stderr, str, ap);
	fflush(stderr);
	}
#endif

	vsyslog(SYSLOG_INFO, str, ap);

	va_end(ap);
}
